Quentin Roche / ST_I2S

Dependents:   X_NUCLEO_CCA02M1

Fork of ST_I2S by ST

drivers/I2S.cpp

Committer:
Wolfgang Betz
Date:
2016-12-22
Revision:
10:1a612c2e4a85
Parent:
9:c4c2240e06d6
Child:
11:50562a5f8a93

File content as of revision 10:1a612c2e4a85:

#include "drivers/I2S.h"
#include "platform/critical.h"
#include "platform/mbed_assert.h"

#if DEVICE_I2S

namespace mbed {

/* betzw - WAS
   #if TRANSACTION_QUEUE_SIZE_I2S
   CircularBuffer<Transaction<I2S>, TRANSACTION_QUEUE_SIZE_I2S> I2S::_transaction_buffer;
   #endif
*/

I2S* I2S::_owner = NULL;
SingletonPtr<PlatformMutex> I2S::_mutex; // intentional class level lock!

events::EventQueue I2S::i2s_bh_queue;

void I2S::lock() {
#if defined(NDEBUG) || !defined(MBED_CONF_RTOS_PRESENT)
    _mutex->lock(); // intentional class level lock!
#else
    osStatus ret = _mutex->lock(); // intentional class level lock!
    MBED_ASSERT(ret == osOK);
#endif
}

void I2S::unlock() {
#if defined(NDEBUG) || !defined(MBED_CONF_RTOS_PRESENT)
    _mutex->unlock(); // intentional class level lock!
#else
    osStatus ret = _mutex->unlock(); // intentional class level lock!
    MBED_ASSERT(ret == osOK);
#endif
}

I2S::I2S(PinName dpin, PinName clk, PinName wsel, PinName fdpin, PinName mck) :
_i2s(),
    _irq_tx(this), _irq_rx(this),
    _priority(MEDIUM),
    _dbits(16),
    _fbits(16),
    _polarity(0),
    _protocol(PHILIPS),
    _mode(MASTER_TX),
    _circular(false),
    _hz(44100) {
    lock();
    /* Init instance */
    i2s_init(&_i2s, dpin, clk, wsel, fdpin, mck, _mode);
    acquire();
    unlock();
}

int I2S::format(int dbits, int fbits, int polarity) {
    lock();
    if (i2s_active(&_i2s)) {
	unlock();
	return -1;
    }
    _dbits = dbits;
    _fbits = fbits;
    _polarity = polarity;
    I2S::_owner = NULL; // Not that elegant, but works. rmeyer
    acquire();
    unlock();
    return 0;
}

int I2S::audio_frequency(unsigned int hz) {
    lock();
    if (i2s_active(&_i2s)) {
	unlock();
	return -1;
    }
    _hz = hz;
    I2S::_owner = NULL; // Not that elegant, but works. rmeyer
    acquire();
    unlock();
    return 0;
}

int I2S::set_protocol(i2s_bitorder_t protocol) {
    lock();
    if (i2s_active(&_i2s)) {
	unlock();
	return -1;
    }
    _protocol = protocol;
    I2S::_owner = NULL; // Not that elegant, but works. rmeyer
    acquire();
    unlock();
    return 0;
}

int I2S::set_mode(i2s_mode_t mode, bool circular) {
    lock();
    if (i2s_active(&_i2s)) {
	unlock();
	return -1;
    }
    _mode = mode;
    _circular = circular;
    I2S::_owner = NULL; // Not that elegant, but works. rmeyer
    acquire();
    unlock();
    return 0;
}

int I2S::harmonize(I2S &dev_i2s_1, I2S &dev_i2s_2) {
    dev_i2s_1.lock();
    if (i2s_active(&dev_i2s_1._i2s)) {
	dev_i2s_1.unlock();
	return -1;
    }

    dev_i2s_2.lock();
    if (i2s_active(&dev_i2s_2._i2s)) {
	dev_i2s_1.unlock();
	return -1;
    }

    uint32_t hz1 = dev_i2s_1._hz;
    uint32_t hz2 = dev_i2s_2._hz;
    int8_t ret = i2s_harmonize(&dev_i2s_1._i2s, &hz1, &dev_i2s_2._i2s, &hz2);

    if(ret == 0) {
	dev_i2s_1.audio_frequency(hz1);
	dev_i2s_2.audio_frequency(hz2);
    }

    dev_i2s_2.unlock();
    dev_i2s_1.unlock();

    return ret;
}

void I2S::abort_transfer()
{
    lock();
    i2s_abort_asynch(&_i2s);
#if TRANSACTION_QUEUE_SIZE_I2S
    dequeue_transaction();
#endif
    unlock();
}


void I2S::clear_transfer_buffer()
{
#if TRANSACTION_QUEUE_SIZE_I2S
    lock();
    _transaction_buffer.reset();
    unlock();
#endif
}

void I2S::abort_all_transfers()
{
    lock();
    clear_transfer_buffer();
    abort_transfer();
    unlock();
}

int I2S::get_transfer_status()
{
    lock();
    if (i2s_active(&_i2s)) {
	unlock();
	return -1;
    }
    unlock();
    return  0;
}

unsigned int I2S::get_module()
{
    return i2s_get_module(&_i2s);
}

int I2S::set_dma_priority(i2s_dma_prio_t prio)
{
    lock();
    if (i2s_active(&_i2s)) {
	unlock();
	return -1;
    }
    _priority = prio;
    unlock();
    return  0;
}

int I2S::queue_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, const event_callback_t& callback, int event)
{ // betzw: MUST be called with lock held!
#if TRANSACTION_QUEUE_SIZE_I2S
    transaction_t t;

    t.tx_buffer = const_cast<void *>(tx_buffer);
    t.tx_length = tx_length;
    t.rx_buffer = rx_buffer;
    t.rx_length = rx_length;
    t.event = event;
    t.callback = callback;
    t.width = 16;
    Transaction<I2S> transaction(this, t);
    core_util_critical_section_enter();
    if (_transaction_buffer.full()) {
	core_util_critical_section_enter();
	return -1; // the buffer is full
    } else {
	_transaction_buffer.push(transaction);
	core_util_critical_section_exit();
	// betzw - seems to be redundant - WAS: dequeue_transaction();
	return 0;
    }
#else
    return -1;
#endif
}


// ignore the fact that there are multiple physical i2s's, and always update if it wasn't us last
void I2S::acquire() { // betzw: MUST be called with lock held!
    if (_owner != this) {
	i2s_format(&_i2s, _dbits, _fbits, _polarity);
	i2s_audio_frequency(&_i2s, _hz);
	i2s_set_protocol(&_i2s, _protocol);
	i2s_set_mode(&_i2s, _mode);
	_owner = this;
    }
}

void I2S::start_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, 
			 const event_callback_t& callback, int event)
{ // betzw: MUST be called with lock held!
    acquire();
    _callback = callback;
    _irq_tx.callback(&I2S::irq_handler_asynch_tx);
    _irq_rx.callback(&I2S::irq_handler_asynch_rx);
    i2s_transfer(&_i2s,
		 const_cast<void *>(tx_buffer), tx_length, rx_buffer, rx_length,
		 _circular, _priority,
		 _irq_tx.entry(), _irq_rx.entry(),
		 event);
}

#if TRANSACTION_QUEUE_SIZE_I2S

void I2S::start_transaction(transaction_t *data)
{ // betzw: MUST be called with lock held!
    start_transfer(data->tx_buffer, data->tx_length, data->rx_buffer, data->rx_length, data->callback, data->event);
}

void I2S::dequeue_transaction()
{
    lock();
    if (!i2s_active(&_i2s)) {
	Transaction<I2S> t;
	if (_transaction_buffer.pop(t)) {
	    I2S* obj = t.get_object();
	    transaction_t* data = t.get_transaction();
	    MBED_ASSERT(obj == this); // betzw: what if 'obj' is NOT equal to 'this'?
	    obj->start_transaction(data);
	}
    }
    unlock();
}

#endif

void I2S::irq_handler_asynch_rx(void)
{
    int event = i2s_irq_handler_asynch(&_i2s, I2S_RX_EVENT);
    if (_callback && (event & I2S_EVENT_ALL)) {
	I2sBhHandler::i2s_defer_function(_callback, event & I2S_EVENT_ALL);
    }
#if TRANSACTION_QUEUE_SIZE_I2S
    if (event & I2S_EVENT_INTERNAL_TRANSFER_COMPLETE) {
	I2sBhHandler::i2s_defer_function(Callback<void()>(this, &I2S::dequeue_transaction));
    }
#endif
}

void I2S::irq_handler_asynch_tx(void)
{
    int event = i2s_irq_handler_asynch(&_i2s, I2S_TX_EVENT);
    if (_callback && (event & I2S_EVENT_ALL)) {
	I2sBhHandler::i2s_defer_function(_callback, event & I2S_EVENT_ALL);
    }
#if TRANSACTION_QUEUE_SIZE_I2S
    if (event & I2S_EVENT_INTERNAL_TRANSFER_COMPLETE) {
	I2sBhHandler::i2s_defer_function(Callback<void()>(this, &I2S::dequeue_transaction));
    }
#endif
}

} // namespace mbed

#endif