/** * \file * * \brief SAM I2S - Inter-IC Sound Controller * * Copyright (C) 2014-2015 Atmel Corporation. All rights reserved. * * \asf_license_start * * \page License * * 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. The name of Atmel may not be used to endorse or promote products derived * from this software without specific prior written permission. * * 4. This software may only be redistributed and used in connection with an * Atmel microcontroller product. * * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. * * \asf_license_stop * */ /* * Support and FAQ: visit Atmel Support */ #include "i2s_callback.h" struct i2s_module *_i2s_instances[I2S_INST_NUM]; static void _i2s_interrupt_handler(const uint8_t instance) { struct i2s_module *module = _i2s_instances[instance]; struct i2s_serializer_module *data_module; /* Get interrupt flags */ uint32_t intflag = module->hw->INTFLAG.reg; uint32_t inten = intflag & module->hw->INTENSET.reg; uint32_t run_flags = (I2S_INTFLAG_TXUR0 | I2S_INTFLAG_RXOR0); uint32_t ready_flags = (I2S_INTFLAG_TXRDY0 | I2S_INTFLAG_RXRDY0); uint32_t call_mask; uint8_t serializer; for (serializer = 0; serializer < 2; serializer ++) { data_module = &module->serializer[serializer]; call_mask = data_module->registered_callback_mask & data_module->enabled_callback_mask; if (intflag & (run_flags | ready_flags)) { /* Serializer Tx ready */ if ((I2S_INTFLAG_TXRDY0 << serializer) & inten) { if (data_module->transferred_words < data_module->requested_words) { /* Write data word */ while (module->hw->SYNCBUSY.reg & (I2S_SYNCBUSY_DATA0 << serializer)) { /* Wait sync */ } switch(data_module->data_size) { case I2S_DATA_SIZE_32BIT: case I2S_DATA_SIZE_24BIT: case I2S_DATA_SIZE_20BIT: case I2S_DATA_SIZE_18BIT: module->hw->DATA[serializer].reg = ((uint32_t*)data_module->job_buffer) \ [data_module->transferred_words]; break; case I2S_DATA_SIZE_16BIT: case I2S_DATA_SIZE_16BIT_COMPACT: module->hw->DATA[serializer].reg = ((uint16_t*)data_module->job_buffer) \ [data_module->transferred_words]; break; default: module->hw->DATA[serializer].reg = ((uint8_t*)data_module->job_buffer) \ [data_module->transferred_words]; } /* Clear interrupt status */ module->hw->INTFLAG.reg = I2S_INTFLAG_TXRDY0 << serializer; /* Count data */ data_module->transferred_words ++; } /* Check if the buffer is done */ if (data_module->transferred_words >= data_module->requested_words) { /* It's done */ data_module->job_status = STATUS_OK; /* Disable interrupt */ module->hw->INTENCLR.reg = I2S_INTFLAG_TXRDY0 << serializer; /* Invoke callback */ if ((1 << I2S_SERIALIZER_CALLBACK_BUFFER_DONE) & call_mask) { (data_module->callback \ [I2S_SERIALIZER_CALLBACK_BUFFER_DONE])(module); } } return; } /* Serializer Rx ready */ if ((I2S_INTFLAG_RXRDY0 << serializer) & inten) { /* Read data word */ switch(data_module->data_size) { case I2S_DATA_SIZE_32BIT: case I2S_DATA_SIZE_24BIT: case I2S_DATA_SIZE_20BIT: case I2S_DATA_SIZE_18BIT: ((uint32_t*)data_module->job_buffer) \ [data_module->transferred_words] = module->hw->DATA[serializer].reg; break; case I2S_DATA_SIZE_16BIT: case I2S_DATA_SIZE_16BIT_COMPACT: ((uint16_t*)data_module->job_buffer) \ [data_module->transferred_words] = (uint16_t)module->hw->DATA[serializer].reg; break; default: ((uint8_t*)data_module->job_buffer) \ [data_module->transferred_words] = (uint8_t)module->hw->DATA[serializer].reg; } /* Clear interrupt status */ module->hw->INTFLAG.reg = I2S_INTFLAG_RXRDY0 << serializer; /* Count data */ data_module->transferred_words ++; /* Check if the buffer is done */ if (data_module->transferred_words >= data_module->requested_words) { if (data_module->job_status == STATUS_BUSY) { data_module->job_status = STATUS_OK; /* Disable interrupt */ module->hw->INTENCLR.reg = I2S_INTFLAG_RXRDY0 << serializer; /* Invoke callback */ if ((1 << I2S_SERIALIZER_CALLBACK_BUFFER_DONE) & call_mask) { (data_module->callback \ [I2S_SERIALIZER_CALLBACK_BUFFER_DONE])(module); } } } return; } /* Serializer Tx undrerun or Rx overrun */ if (run_flags & inten) { module->hw->INTFLAG.reg = I2S_INTFLAG_TXUR0 << serializer; if ((1 << I2S_SERIALIZER_CALLBACK_OVER_UNDER_RUN) & call_mask) { (data_module->callback \ [I2S_SERIALIZER_CALLBACK_OVER_UNDER_RUN])(module); } return; } } run_flags <<= 1; ready_flags <<= 1; } } /** Interrupt handler for the I2S module */ void I2S_Handler(void) { _i2s_interrupt_handler(0); } /** * \brief Write buffer to the specified Serializer of I2S module * * \param[in] module_inst Pointer to the software module instance struct * \param[in] serializer The serializer to write to * \param[in] buffer The data buffer to write * \param[in] size Number of data words to write * * \return Status of the initialization procedure. * * \retval STATUS_OK The data was sent successfully * \retval STATUS_ERR_DENIED The serializer is not in transmit mode * \retval STATUS_ERR_INVALID_ARG An invalid buffer pointer was supplied */ enum status_code i2s_serializer_write_buffer_job( struct i2s_module *const module_inst, const enum i2s_serializer serializer, const void *buffer, const uint32_t size) { struct i2s_serializer_module *data_module; /* Sanity check arguments */ Assert(module_inst); Assert(module_inst->hw); Assert(serializer < I2S_SERIALIZER_N); data_module = &module_inst->serializer[serializer]; /* Serializer must in transmit mode */ if (data_module->mode != I2S_SERIALIZER_TRANSMIT) { return STATUS_ERR_DENIED; } /* Buffer should be aligned */ switch(data_module->data_size) { case I2S_DATA_SIZE_32BIT: case I2S_DATA_SIZE_24BIT: case I2S_DATA_SIZE_20BIT: case I2S_DATA_SIZE_18BIT: if ((uint32_t)buffer & 0x3) { return STATUS_ERR_INVALID_ARG; } break; case I2S_DATA_SIZE_16BIT: case I2S_DATA_SIZE_16BIT_COMPACT: if ((uint32_t)buffer & 0x1) { return STATUS_ERR_INVALID_ARG; } break; default: break; } data_module = &module_inst->serializer[serializer]; if (data_module->job_status == STATUS_BUSY) { return STATUS_BUSY; } data_module->job_status = STATUS_BUSY; data_module->requested_words = size; data_module->transferred_words = 0; data_module->job_buffer = (void*)buffer; module_inst->hw->INTENSET.reg = (I2S_INTENSET_TXRDY0 << serializer); return STATUS_OK; } /** * \brief Read from the specified Serializer of I2S module to a buffer * * \param[in] module_inst Pointer to the software module instance struct * \param[in] serializer The serializer to write to * \param[out] buffer The buffer to fill read data * \param[in] size Number of data words to read * * \return Status of the initialization procedure. * * \retval STATUS_OK The data was sent successfully * \retval STATUS_ERR_DENIED The serializer is not in receive mode * \retval STATUS_ERR_INVALID_ARG An invalid buffer pointer was supplied */ enum status_code i2s_serializer_read_buffer_job( struct i2s_module *const module_inst, const enum i2s_serializer serializer, void *buffer, const uint32_t size) { struct i2s_serializer_module *data_module; /* Sanity check arguments */ Assert(module_inst); Assert(module_inst->hw); Assert(serializer < I2S_SERIALIZER_N); data_module = &module_inst->serializer[serializer]; /* Serializer must in receive mode */ if (data_module->mode == I2S_SERIALIZER_TRANSMIT) { return STATUS_ERR_DENIED; } /* Data buffer must be aligned */ switch(data_module->data_size) { case I2S_DATA_SIZE_32BIT: case I2S_DATA_SIZE_24BIT: case I2S_DATA_SIZE_20BIT: case I2S_DATA_SIZE_18BIT: if ((uint32_t)buffer & 0x3) { return STATUS_ERR_INVALID_ARG; } break; case I2S_DATA_SIZE_16BIT: case I2S_DATA_SIZE_16BIT_COMPACT: if ((uint32_t)buffer & 0x1) { return STATUS_ERR_INVALID_ARG; } break; default: break; } data_module = &module_inst->serializer[serializer]; if (data_module->job_status == STATUS_BUSY) { return STATUS_BUSY; } data_module->job_status = STATUS_BUSY; data_module->requested_words = size; data_module->transferred_words = 0; data_module->job_buffer = (void*)buffer; module_inst->hw->INTENCLR.reg = (I2S_INTENSET_RXRDY0 << serializer); module_inst->hw->INTENSET.reg = (I2S_INTENSET_RXRDY0 << serializer); return STATUS_OK; } /** * \brief Aborts an ongoing job running on serializer * * Aborts an ongoing job. * * \param[in] module_inst Pointer to the software module instance struct * \param[in] serializer The serializer which runs the job * \param[in] job_type Type of job to abort */ void i2s_serializer_abort_job( struct i2s_module *const module_inst, const enum i2s_serializer serializer, const enum i2s_job_type job_type) { /* Sanity check arguments */ Assert(module_inst); Assert(module_inst->hw); Assert(serializer < I2S_SERIALIZER_N); if (job_type == I2S_JOB_WRITE_BUFFER) { /* Disable interrupt */ module_inst->hw->INTENCLR.reg = (I2S_INTENCLR_TXRDY0 << serializer); /* Mark job as aborted */ module_inst->serializer[serializer].job_status = STATUS_ABORTED; } else if (job_type == I2S_JOB_READ_BUFFER) { /* Disable interrupt */ module_inst->hw->INTENCLR.reg = (I2S_INTENCLR_RXRDY0 << serializer); /* Mark job as aborted */ module_inst->serializer[serializer].job_status = STATUS_ABORTED; } } /** * \brief Gets the status of a job running on serializer * * Gets the status of an ongoing or the last job. * * \param[in] module_inst Pointer to the software module instance struct * \param[in] serializer The serializer which runs the job * \param[in] job_type Type of job to abort * * \return Status of the job. */ enum status_code i2s_serializer_get_job_status( const struct i2s_module *const module_inst, const enum i2s_serializer serializer, const enum i2s_job_type job_type) { /* Sanity check arguments */ Assert(module_inst); Assert(serializer < I2S_SERIALIZER_N); if (job_type == I2S_JOB_WRITE_BUFFER || job_type == I2S_JOB_READ_BUFFER) { return module_inst->serializer[serializer].job_status; } else { return STATUS_ERR_INVALID_ARG; } }