/*! * Copyright (C) 2008 by egnite GmbH. * Copyright (C) 2007 by egnite Software GmbH. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /*! * \file tlv320dac.c * \brief DAC routines. * * \verbatim * $Id: tlv320dac.c 4473 2012-08-20 15:12:45Z haraldkipp $ * \endverbatim */ #include #include #include #include #include #include #include #include #ifndef TWI_SLA_DAC #define TWI_SLA_DAC 0x1A #endif /* * Initial volume. */ #ifndef TLV320DAC_VOL #define TLV320DAC_VOL 0x18 #endif /* * Number of PCM buffers. */ #ifndef SAMPLE_BUFFERS #if defined (AT91SAM9260_EK) #define SAMPLE_BUFFERS 32 #else #define SAMPLE_BUFFERS 3 #endif #endif /* * Select TWI pins for bit-banging */ #if defined(AT91SAM9260_EK) #ifndef TWI_SCL_BASE #define TWI_SCL_BASE PIOB_BASE #endif #ifndef TWI_SDA_BASE #define TWI_SDA_BASE PIOB_BASE #endif #ifndef TWI_SCL_BIT #define TWI_SCL_BIT 13 #endif #ifndef TWI_SDA_BIT #define TWI_SDA_BIT 12 #endif #ifndef TWI_DELAY #define TWI_DELAY 16 #endif #elif defined(AT91SAM7X_EK) #ifndef TWI_SCL_BASE #define TWI_SCL_BASE PIOA_BASE #endif #ifndef TWI_SDA_BASE #define TWI_SDA_BASE PIOA_BASE #endif #ifndef TWI_SCL_BIT #define TWI_SCL_BIT 11 #endif #ifndef TWI_SDA_BIT #define TWI_SDA_BIT 10 #endif #endif /* * Select I2S pins. */ #if defined (MCU_AT91SAM9260) /* _EK */ #define DACI2S_PIO_ID PIOB_ID #define DACI2S_PINS_A _BV(PB18_TD0_A) | _BV(PB17_TF0_A) | _BV(PB16_TK0_A) #define DACI2S_PINS_B 0 #else /* _EK */ #define DACI2S_PIO_ID PIOA_ID #define DACI2S_PINS_A _BV(PA23_TD_A) | _BV(PA21_TF_A) | _BV(PA22_TK_A) #define DACI2S_PINS_B 0 #endif /* _EK */ /* * Determine PIO used by I2S. */ #if DACI2S_PIO_ID == PIOA_ID /* DACI2S_PIO_ID */ #define DACI2S_PDR PIOA_PDR #define DACI2S_ASR PIOA_ASR #define DACI2S_BSR PIOA_BSR #elif DACI2S_PIO_ID == PIOB_ID /* DACI2S_PIO_ID */ #define DACI2S_PDR PIOB_PDR #define DACI2S_ASR PIOB_ASR #define DACI2S_BSR PIOB_BSR #endif /* DACI2S_PIO_ID */ /*! * \brief I2S event queue. */ static HANDLE i2s_que; /*! * \brief Number of channels. */ #define PCM_CHANS 2 /*! * \brief Number of bits per sample. */ #define PCM_BITS 16 /*! * \brief PCM buffer type. */ typedef struct _PCM_BUFFER { u_short *wbf_dat; /*!< Pointer to PCM data. */ int wbf_siz; /*!< Buffer size. */ int wbf_len; /*!< Number of valid bytes buffered. */ } PCM_BUFFER; /* PCM buffer queue. */ static PCM_BUFFER pcm_bufq[SAMPLE_BUFFERS]; /* PCM buffer read index. */ static volatile unsigned int brd_idx; /* PCM buffer read position. */ static volatile int brd_pos; /* PCM buffer write index. */ static unsigned int bwr_idx; #if defined(TWI_SCL_BASE) && defined(TWI_SDA_BASE) #define TWI_ENABLE() { \ outr(TWI_SCL_BASE + PIO_SODR_OFF, _BV(TWI_SCL_BIT)); \ outr(TWI_SCL_BASE + PIO_PER_OFF, _BV(TWI_SCL_BIT)); \ outr(TWI_SCL_BASE + PIO_OER_OFF, _BV(TWI_SCL_BIT)); \ outr(TWI_SDA_BASE + PIO_SODR_OFF, _BV(TWI_SDA_BIT)); \ outr(TWI_SDA_BASE + PIO_PER_OFF, _BV(TWI_SDA_BIT)); \ } #define SDA_LOW() { \ outr(TWI_SDA_BASE + PIO_CODR_OFF, _BV(TWI_SDA_BIT)); \ outr(TWI_SDA_BASE + PIO_OER_OFF, _BV(TWI_SDA_BIT)); \ } #define SDA_HIGH() { \ outr(TWI_SDA_BASE + PIO_SODR_OFF, _BV(TWI_SDA_BIT)); \ outr(TWI_SDA_BASE + PIO_ODR_OFF, _BV(TWI_SDA_BIT)); \ } #define SCL_LOW() outr(TWI_SCL_BASE + PIO_CODR_OFF, _BV(TWI_SCL_BIT)) #define SCL_HIGH() outr(TWI_SCL_BASE + PIO_SODR_OFF, _BV(TWI_SCL_BIT)) #define SDA_STAT() (inr(TWI_SCL_BASE + PIO_PDSR_OFF) & _BV(TWI_SDA_BIT)) #else #define TWI_ENABLE() #define SDA_LOW() #define SDA_HIGH() #define SCL_LOW() #define SCL_HIGH() #define SDA_STAT() (1) #endif #ifndef TWI_DELAY #define TWI_DELAY 8 #endif /* * Short delay. * * Our bit banging code relies on pull-up resistors. The I/O ports mimic * open collector outputs by switching to input mode for high level and * switching to output mode for low level. This is much slower than * switching an output between low to high. Thus we need some delay. */ static void TwBitDelay(int nops) { while (nops--) { _NOP(); } } /* * Falling edge on the data line while the clock line is high indicates * a start condition. * * Entry: SCL any, SDA any * Exit: SCL low, SDA low */ static void TwBitStart(void) { SDA_HIGH(); TwBitDelay(TWI_DELAY); SCL_HIGH(); TwBitDelay(TWI_DELAY); SDA_LOW(); TwBitDelay(TWI_DELAY); SCL_LOW(); TwBitDelay(TWI_DELAY); } /* * Rising edge on the data line while the clock line is high indicates * a stop condition. * * Entry: SCL low, SDA any * Exit: SCL high, SDA high */ static void TwBitStop(void) { SDA_LOW(); TwBitDelay(TWI_DELAY); SCL_HIGH(); TwBitDelay(2 * TWI_DELAY); SDA_HIGH(); TwBitDelay(8 * TWI_DELAY); } /* * Toggles out a single byte in master mode. * * Entry: SCL low, SDA any * Exit: SCL low, SDA high */ static int TwBitPut(uint8_t octet) { int i; for (i = 0x80; i; i >>= 1) { /* Set the data bit. */ if (octet & i) { SDA_HIGH(); } else { SDA_LOW(); } /* Wait for data to stabelize. */ TwBitDelay(TWI_DELAY); /* Toggle the clock. */ SCL_HIGH(); TwBitDelay(2 * TWI_DELAY); SCL_LOW(); TwBitDelay(TWI_DELAY); } /* Set data line high to receive the ACK bit. */ SDA_HIGH(); /* ACK should appear shortly after the clock's rising edge. */ SCL_HIGH(); TwBitDelay(2 * TWI_DELAY); if (SDA_STAT()) { i = -1; } else { i = 0; } SCL_LOW(); return i; } /* * Toggles in a single byte in master mode. * * Entry: SCL low, SDA any * Exit: SCL low, SDA high */ static uint8_t TwBitGet(void) { uint8_t rc = 0; int i; /* SDA is input. */ SDA_HIGH(); TwBitDelay(TWI_DELAY); for (i = 0x80; i; i >>= 1) { TwBitDelay(TWI_DELAY); /* Data should appear shortly after the clock's rising edge. */ SCL_HIGH(); TwBitDelay(2 * TWI_DELAY); /* SDA read. */ if (SDA_STAT()) { rc |= i; } SCL_LOW(); } return rc; } /* * Toggles out an acknowledge bit in master mode. * * Entry: SCL low, SDA any * Exit: SCL low, SDA high */ static void TwBitAck(void) { SDA_LOW(); TwBitDelay(TWI_DELAY); SCL_HIGH(); TwBitDelay(2 * TWI_DELAY); SCL_LOW(); TwBitDelay(TWI_DELAY); SDA_HIGH(); } static int TwBitInit(uint8_t sla) { TWI_ENABLE(); return 0; } static int TwBitMasterTransact(uint8_t sla, const void *txdata, uint16_t txlen, void *rxdata, uint16_t rxsiz, uint32_t tmo) { int rc = 0; uint8_t *cp; if (txlen) { TwBitStart(); /* Send SLA+W and check for ACK. */ if ((rc = TwBitPut(sla << 1)) == 0) { for (cp = (uint8_t *)txdata; txlen--; cp++) { if ((rc = TwBitPut(*cp)) != 0) { break; } } } } if (rc == 0 && rxsiz) { TwBitStart(); /* Send SLA+R and check for ACK. */ if ((rc = TwBitPut((sla << 1) | 1)) == 0) { for (cp = rxdata;; cp++) { *cp = TwBitGet(); if (++rc >= rxsiz) { break; } TwBitAck(); } } } TwBitStop(); return rc; } /*! * \brief Set PDC pointer and counter registers. * * SSC interrupts must have been disabled befor calling this function. */ static void I2sPdcFill(void) { if (brd_idx != bwr_idx) { if (inr(SSC_TNCR) == 0) { if (++brd_idx >= SAMPLE_BUFFERS) { brd_idx = 0; } outr(SSC_TNPR, (unsigned int) pcm_bufq[brd_idx].wbf_dat); outr(SSC_TNCR, pcm_bufq[brd_idx].wbf_len); } } } /*! * \brief SSC interrupt handler. * * Called when PDC transmission done. */ static void I2sInterrupt(void *arg) { I2sPdcFill(); NutEventPostFromIrq(&i2s_que); } /*! * \brief Read value from specified DAC register. * * Not implemented, because the TLV320AIC23B is a write-only device. * * \param reg DAC register address. * * \return Always 0xFF. */ u_char Tlv320DacReadReg(unsigned int reg) { return 0xFF; } /*! * \brief Write value to specified DAC register. * * Communicates with the DAC chip via TWI. * * \param reg DAC register address. * \param val Value to store in specified register. */ void Tlv320DacWriteReg(unsigned int reg, unsigned int val) { u_char txdata[2]; txdata[0] = (u_char)(reg << 1) | (u_char)(val >> 8); txdata[1] = (u_char)val; TwBitMasterTransact(TWI_SLA_DAC, txdata, 2, NULL, 0, 0); } /*! * \brief Enable I2S interface with a given rate. * * \param rate PCM sample rate in Hertz. * * \return Always 0. */ static int Tlv320I2sEnable(unsigned int rate) { /* Enable SSC clock. */ outr(PMC_PCER, _BV(SSC_ID)); /* Select SSC peripheral functions. */ outr(DACI2S_ASR, DACI2S_PINS_A); outr(DACI2S_BSR, DACI2S_PINS_B); /* Enable SSC peripheral pins. */ outr(DACI2S_PDR, DACI2S_PINS_A | DACI2S_PINS_B); /* Configure 16-bit stereo I2S transmit format. */ outr(SSC_CMR, 0); outr(SSC_TCMR, /* Set transmit clock mode. */ SSC_CKS_PIN | /* Use external clock at TK. */ SSC_START_EDGE_RF | /* Start transmission on any edge. */ (1 << SSC_STTDLY_LSB)); /* Delay start by 1 cycle. */ outr(SSC_TFMR, /* Set transmit frame mode. */ ((PCM_BITS - 1) << SSC_DATLEN_LSB) | /* Transmit 16 bits. */ SSC_MSBF); /* Most significant bit first. */ /* Enable transmitter in PDC mode. */ outr(SSC_PTCR, PDC_TXTEN); outr(SSC_CR, SSC_TXEN); return 0; } /*! * \brief Disable I2S interface. * * \return Always 0. */ static int Tlv320I2sDisable(void) { /* Disable all interrupts. */ outr(SSC_IDR, 0xFFFFFFFF); /* Disable SSC interrupt. */ NutIrqDisable(&sig_SSC); /* Disable SSC clock. */ outr(PMC_PCDR, _BV(SSC_ID)); /* Reset receiver and transmitter. */ outr(SSC_CR, SSC_SWRST | SSC_RXDIS | SSC_TXDIS); outr(SSC_RCMR, 0); outr(SSC_RFMR, 0); outr(SSC_PTCR, PDC_RXTDIS); outr(SSC_PTCR, PDC_TXTDIS); outr(SSC_TNCR, 0); outr(SSC_TCR, 0); return 0; } /*! * \brief Configure the SSC for I2S mode. * * \param rate Sampling rate in Hertz. * * \return Always 0. */ static int Tlv320I2sInit(unsigned int rate) { /* Register SSC interrupt handler. */ NutRegisterIrqHandler(&sig_SSC, I2sInterrupt, 0); Tlv320I2sDisable(); Tlv320I2sEnable(rate); /* Enable SSC interrupt. */ NutIrqEnable(&sig_SSC); return 0; } int Tlv320DacSetRate(unsigned int rate) { switch(rate) { case 8000: #ifdef AT91SAM7X_EK Tlv320DacWriteReg(DAC_SRATE, (3 << DAC_SRATE_SR_LSB)); #else Tlv320DacWriteReg(DAC_SRATE, (3 << DAC_SRATE_SR_LSB) | DAC_SRATE_USB); #endif break; case 8021: Tlv320DacWriteReg(DAC_SRATE, (11 << DAC_SRATE_SR_LSB) | DAC_SRATE_BOSR | DAC_SRATE_USB); break; case 44100: Tlv320DacWriteReg(DAC_SRATE, (8 << DAC_SRATE_SR_LSB) | DAC_SRATE_BOSR | DAC_SRATE_USB); break; case 48000: Tlv320DacWriteReg(DAC_SRATE, (0 << DAC_SRATE_SR_LSB) | DAC_SRATE_USB); break; case 88200: Tlv320DacWriteReg(DAC_SRATE, (15 << DAC_SRATE_SR_LSB) | DAC_SRATE_BOSR | DAC_SRATE_USB); break; case 96000: Tlv320DacWriteReg(DAC_SRATE, (7 << DAC_SRATE_SR_LSB) | DAC_SRATE_USB); break; default: return -1; } return 0; } /*! * \brief Initialize TLV320AIC23B DAC interface. * * \param rate Sample rate. * * \return 0 on success, -1 otherwise. */ int Tlv320DacInit(unsigned int rate) { /* Initialize TWI. */ TwBitInit(0); Tlv320DacWriteReg(DAC_RESET, 0); /* Power down line in. */ Tlv320DacWriteReg(DAC_PWRDN, DAC_PWRDN_LINE); Tlv320DacWriteReg(DAC_PWRDN, 0); /* Set sampling rate. */ if (Tlv320DacSetRate(rate)) { Tlv320DacWriteReg(DAC_RESET, 0); return -1; } Tlv320DacWriteReg(DAC_ANA_PATH, DAC_ANA_PATH_DAC | DAC_ANA_PATH_INSEL | DAC_ANA_PATH_MICB); Tlv320DacWriteReg(DAC_DIG_PATH, 0); /* I2S master. */ Tlv320DacWriteReg(DAC_DAI_FMT, DAC_DAI_FMT_MS | DAC_DAI_FMT_FOR_I2S); Tlv320DacWriteReg(DAC_DI_ACT, DAC_DI_ACT_ACT); Tlv320DacWriteReg(DAC_LHP_VOL, DAC_LHP_VOL_LRS | (0x60 << DAC_LHP_VOL_LHV_LSB)); /* Initialize I2S. */ return Tlv320I2sInit(rate); } /*! * \brief Start transmitting audio samples. * * \return Always 0. */ static int Tlv320DacStart(void) { NutIrqDisable(&sig_SSC); outr(SSC_IDR, SSC_TXEMPTY); /* Enable transmitter in PDC mode. */ outr(SSC_PTCR, PDC_TXTEN); I2sPdcFill(); outr(SSC_IER, SSC_ENDTX); /* Enable transmitter. */ outr(SSC_CR, SSC_TXEN); NutIrqEnable(&sig_SSC); return 0; } /*! * \brief Wait until all buffered samples have been transmitted. * * \return Always 0. */ int Tlv320DacFlush(void) { int rc = 0; while (bwr_idx != brd_idx) { if ((rc = NutEventWait(&i2s_que, 100)) != 0) { Tlv320DacStart(); } } return rc; } /*! * \brief Add audio samples to the TLV320AIC23B transmit queue. * * \param buf Points to PCM data. * \param len Number of samples. * * \return 0 on success or -1 if out of memory. */ int Tlv320DacWrite(void *buf, int len) { unsigned int idx; /* Move to the next buffer to write to. */ idx = bwr_idx + 1; if (idx >= SAMPLE_BUFFERS) { idx = 0; } /* If all buffers are filled, wait for an event posted by the interrupt routine. */ while (idx == brd_idx) { if (NutEventWait(&i2s_que, 100)) { Tlv320DacStart(); } } /* * Check, if the current buffer size is too small or not allocated. */ if (pcm_bufq[idx].wbf_siz < len) { if (pcm_bufq[idx].wbf_siz) { free(pcm_bufq[idx].wbf_dat); pcm_bufq[idx].wbf_siz = 0; } pcm_bufq[idx].wbf_dat = malloc(len * 2); if (pcm_bufq[idx].wbf_dat == NULL) { /* Out of memory. */ return -1; } pcm_bufq[idx].wbf_siz = len; } /* * At this point we got an available buffer with sufficient size. * Move the data to it, set the number of valid bytes and update * the write (producer) index. */ memcpy(pcm_bufq[idx].wbf_dat, buf, len * 2); pcm_bufq[idx].wbf_len = len; bwr_idx = idx; return 0; } /*! * \brief Set volume. * * Sets the master playback gain. Range is +6..-73 dB. * * \param left Left channel gain in dB. * \param right Right channel gain in dB. * * \return 0 on success, -1 otherwise. */ int Tlv320DacSetVolume(int left, int right) { /* Cut to limits. */ if (left > DAC_MAX_VOLUME) { left = DAC_MAX_VOLUME; } else if (left < DAC_MIN_VOLUME) { left = DAC_MIN_VOLUME; } if (right > DAC_MAX_VOLUME) { right = DAC_MAX_VOLUME; } else if (right < DAC_MIN_VOLUME) { right = DAC_MIN_VOLUME; } Tlv320DacWriteReg(DAC_LHP_VOL, (unsigned int)(left + 121)); Tlv320DacWriteReg(DAC_RHP_VOL, (unsigned int)(right + 121)); return 0; }