diff options
Diffstat (limited to 'drivers/mxc/ssi/ssi.c')
-rw-r--r-- | drivers/mxc/ssi/ssi.c | 1221 |
1 files changed, 1221 insertions, 0 deletions
diff --git a/drivers/mxc/ssi/ssi.c b/drivers/mxc/ssi/ssi.c new file mode 100644 index 000000000000..f849a3ec7072 --- /dev/null +++ b/drivers/mxc/ssi/ssi.c @@ -0,0 +1,1221 @@ +/* + * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ssi.c + * @brief This file contains the implementation of the SSI driver main services + * + * + * @ingroup SSI + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <asm/uaccess.h> +#include <mach/clock.h> + +#include "registers.h" +#include "ssi.h" + +static spinlock_t ssi_lock; +struct mxc_audio_platform_data *ssi_platform_data; + +EXPORT_SYMBOL(ssi_ac97_frame_rate_divider); +EXPORT_SYMBOL(ssi_ac97_get_command_address_register); +EXPORT_SYMBOL(ssi_ac97_get_command_data_register); +EXPORT_SYMBOL(ssi_ac97_get_tag_register); +EXPORT_SYMBOL(ssi_ac97_mode_enable); +EXPORT_SYMBOL(ssi_ac97_tag_in_fifo); +EXPORT_SYMBOL(ssi_ac97_read_command); +EXPORT_SYMBOL(ssi_ac97_set_command_address_register); +EXPORT_SYMBOL(ssi_ac97_set_command_data_register); +EXPORT_SYMBOL(ssi_ac97_set_tag_register); +EXPORT_SYMBOL(ssi_ac97_variable_mode); +EXPORT_SYMBOL(ssi_ac97_write_command); +EXPORT_SYMBOL(ssi_clock_idle_state); +EXPORT_SYMBOL(ssi_clock_off); +EXPORT_SYMBOL(ssi_enable); +EXPORT_SYMBOL(ssi_get_data); +EXPORT_SYMBOL(ssi_get_status); +EXPORT_SYMBOL(ssi_i2s_mode); +EXPORT_SYMBOL(ssi_interrupt_disable); +EXPORT_SYMBOL(ssi_interrupt_enable); +EXPORT_SYMBOL(ssi_network_mode); +EXPORT_SYMBOL(ssi_receive_enable); +EXPORT_SYMBOL(ssi_rx_bit0); +EXPORT_SYMBOL(ssi_rx_clock_direction); +EXPORT_SYMBOL(ssi_rx_clock_divide_by_two); +EXPORT_SYMBOL(ssi_rx_clock_polarity); +EXPORT_SYMBOL(ssi_rx_clock_prescaler); +EXPORT_SYMBOL(ssi_rx_early_frame_sync); +EXPORT_SYMBOL(ssi_rx_fifo_counter); +EXPORT_SYMBOL(ssi_rx_fifo_enable); +EXPORT_SYMBOL(ssi_rx_fifo_full_watermark); +EXPORT_SYMBOL(ssi_rx_flush_fifo); +EXPORT_SYMBOL(ssi_rx_frame_direction); +EXPORT_SYMBOL(ssi_rx_frame_rate); +EXPORT_SYMBOL(ssi_rx_frame_sync_active); +EXPORT_SYMBOL(ssi_rx_frame_sync_length); +EXPORT_SYMBOL(ssi_rx_mask_time_slot); +EXPORT_SYMBOL(ssi_rx_prescaler_modulus); +EXPORT_SYMBOL(ssi_rx_shift_direction); +EXPORT_SYMBOL(ssi_rx_word_length); +EXPORT_SYMBOL(ssi_set_data); +EXPORT_SYMBOL(ssi_set_wait_states); +EXPORT_SYMBOL(ssi_synchronous_mode); +EXPORT_SYMBOL(ssi_system_clock); +EXPORT_SYMBOL(ssi_transmit_enable); +EXPORT_SYMBOL(ssi_two_channel_mode); +EXPORT_SYMBOL(ssi_tx_bit0); +EXPORT_SYMBOL(ssi_tx_clock_direction); +EXPORT_SYMBOL(ssi_tx_clock_divide_by_two); +EXPORT_SYMBOL(ssi_tx_clock_polarity); +EXPORT_SYMBOL(ssi_tx_clock_prescaler); +EXPORT_SYMBOL(ssi_tx_early_frame_sync); +EXPORT_SYMBOL(ssi_tx_fifo_counter); +EXPORT_SYMBOL(ssi_tx_fifo_empty_watermark); +EXPORT_SYMBOL(ssi_tx_fifo_enable); +EXPORT_SYMBOL(ssi_tx_flush_fifo); +EXPORT_SYMBOL(ssi_tx_frame_direction); +EXPORT_SYMBOL(ssi_tx_frame_rate); +EXPORT_SYMBOL(ssi_tx_frame_sync_active); +EXPORT_SYMBOL(ssi_tx_frame_sync_length); +EXPORT_SYMBOL(ssi_tx_mask_time_slot); +EXPORT_SYMBOL(ssi_tx_prescaler_modulus); +EXPORT_SYMBOL(ssi_tx_shift_direction); +EXPORT_SYMBOL(ssi_tx_word_length); +EXPORT_SYMBOL(get_ssi_fifo_addr); + +struct resource *res; +unsigned long base_addr_1; +unsigned long base_addr_2; + +unsigned int get_ssi_fifo_addr(unsigned int ssi, int direction) +{ + unsigned int fifo_addr; + if (direction == 1) { + if (ssi_platform_data->ssi_num == 2) { + fifo_addr = + (ssi == + SSI1) ? (int)(base_addr_1 + + MXC_SSI1STX0) : (int)(base_addr_2 + + MXC_SSI2STX0); + } else { + fifo_addr = (int)(base_addr_1 + MXC_SSI1STX0); + } + } else { + fifo_addr = (int)(base_addr_1 + MXC_SSI1SRX0); + } + return fifo_addr; +} + +void *get_ssi_base_addr(unsigned int ssi) +{ + if (ssi_platform_data->ssi_num == 2) { + if (ssi == SSI1) + return IO_ADDRESS(base_addr_1); + else + return IO_ADDRESS(base_addr_2); + } + return IO_ADDRESS(base_addr_1); +} + +void set_register_bits(unsigned int mask, unsigned int data, + unsigned int offset, unsigned int ssi) +{ + volatile unsigned long reg = 0; + void *base_addr = get_ssi_base_addr(ssi); + unsigned long flags = 0; + + spin_lock_irqsave(&ssi_lock, flags); + reg = __raw_readl(base_addr + offset); + reg = (reg & (~mask)) | data; + __raw_writel(reg, base_addr + offset); + spin_unlock_irqrestore(&ssi_lock, flags); +} + +unsigned long getreg_value(unsigned int offset, unsigned int ssi) +{ + void *base_addr = get_ssi_base_addr(ssi); + return __raw_readl(base_addr + offset); +} + +void set_register(unsigned int data, unsigned int offset, unsigned int ssi) +{ + void *base_addr = get_ssi_base_addr(ssi); + __raw_writel(data, base_addr + offset); +} + +/*! + * This function controls the AC97 frame rate divider. + * + * @param module the module number + * @param frame_rate_divider the AC97 frame rate divider + */ +void ssi_ac97_frame_rate_divider(ssi_mod module, + unsigned char frame_rate_divider) +{ + unsigned int reg = 0; + + reg = getreg_value(MXC_SSISACNT, module); + reg |= ((frame_rate_divider & AC97_FRAME_RATE_MASK) + << AC97_FRAME_RATE_DIVIDER_SHIFT); + set_register(reg, MXC_SSISACNT, module); +} + +/*! + * This function gets the AC97 command address register. + * + * @param module the module number + * @return This function returns the command address slot information. + */ +unsigned int ssi_ac97_get_command_address_register(ssi_mod module) +{ + return getreg_value(MXC_SSISACADD, module); +} + +/*! + * This function gets the AC97 command data register. + * + * @param module the module number + * @return This function returns the command data slot information. + */ +unsigned int ssi_ac97_get_command_data_register(ssi_mod module) +{ + return getreg_value(MXC_SSISACDAT, module); +} + +/*! + * This function gets the AC97 tag register. + * + * @param module the module number + * @return This function returns the tag information. + */ +unsigned int ssi_ac97_get_tag_register(ssi_mod module) +{ + return getreg_value(MXC_SSISATAG, module); +} + +/*! + * This function controls the AC97 mode. + * + * @param module the module number + * @param state the AC97 mode state (enabled or disabled) + */ +void ssi_ac97_mode_enable(ssi_mod module, bool state) +{ + unsigned int reg = 0; + + reg = getreg_value(MXC_SSISACNT, module); + if (state == true) { + reg |= (1 << AC97_MODE_ENABLE_SHIFT); + } else { + reg &= ~(1 << AC97_MODE_ENABLE_SHIFT); + } + + set_register(reg, MXC_SSISACNT, module); +} + +/*! + * This function controls the AC97 tag in FIFO behavior. + * + * @param module the module number + * @param state the tag in fifo behavior (Tag info stored in Rx FIFO 0 if true, + * Tag info stored in SATAG register otherwise) + */ +void ssi_ac97_tag_in_fifo(ssi_mod module, bool state) +{ + unsigned int reg = 0; + + reg = getreg_value(MXC_SSISACNT, module); + if (state == true) { + reg |= (1 << AC97_TAG_IN_FIFO_SHIFT); + } else { + reg &= ~(1 << AC97_TAG_IN_FIFO_SHIFT); + } + + set_register(reg, MXC_SSISACNT, module); +} + +/*! + * This function controls the AC97 read command. + * + * @param module the module number + * @param state the next AC97 command is a read command or not + */ +void ssi_ac97_read_command(ssi_mod module, bool state) +{ + unsigned int reg = 0; + + reg = getreg_value(MXC_SSISACNT, module); + if (state == true) { + reg |= (1 << AC97_READ_COMMAND_SHIFT); + } else { + reg &= ~(1 << AC97_READ_COMMAND_SHIFT); + } + + set_register(reg, MXC_SSISACNT, module); +} + +/*! + * This function sets the AC97 command address register. + * + * @param module the module number + * @param address the command address slot information + */ +void ssi_ac97_set_command_address_register(ssi_mod module, unsigned int address) +{ + set_register(address, MXC_SSISACADD, module); +} + +/*! + * This function sets the AC97 command data register. + * + * @param module the module number + * @param data the command data slot information + */ +void ssi_ac97_set_command_data_register(ssi_mod module, unsigned int data) +{ + set_register(data, MXC_SSISACDAT, module); +} + +/*! + * This function sets the AC97 tag register. + * + * @param module the module number + * @param tag the tag information + */ +void ssi_ac97_set_tag_register(ssi_mod module, unsigned int tag) +{ + set_register(tag, MXC_SSISATAG, module); +} + +/*! + * This function controls the AC97 variable mode. + * + * @param module the module number + * @param state the AC97 variable mode state (enabled or disabled) + */ void ssi_ac97_variable_mode(ssi_mod module, bool state) +{ + unsigned int reg = 0; + + reg = getreg_value(MXC_SSISACNT, module); + if (state == true) { + reg |= (1 << AC97_VARIABLE_OPERATION_SHIFT); + } else { + reg &= ~(1 << AC97_VARIABLE_OPERATION_SHIFT); + } + + set_register(reg, MXC_SSISACNT, module); +} + +/*! + * This function controls the AC97 write command. + * + * @param module the module number + * @param state the next AC97 command is a write command or not + */ +void ssi_ac97_write_command(ssi_mod module, bool state) +{ + unsigned int reg = 0; + + reg = getreg_value(MXC_SSISACNT, module); + if (state == true) { + reg |= (1 << AC97_WRITE_COMMAND_SHIFT); + } else { + reg &= ~(1 << AC97_WRITE_COMMAND_SHIFT); + } + + set_register(reg, MXC_SSISACNT, module); +} + +/*! + * This function controls the idle state of the transmit clock port during SSI internal gated mode. + * + * @param module the module number + * @param state the clock idle state + */ +void ssi_clock_idle_state(ssi_mod module, idle_state state) +{ + set_register_bits(1 << SSI_CLOCK_IDLE_SHIFT, + state << SSI_CLOCK_IDLE_SHIFT, MXC_SSISCR, module); +} + +/*! + * This function turns off/on the ccm_ssi_clk to reduce power consumption. + * + * @param module the module number + * @param state the state for ccm_ssi_clk (true: turn off, else:turn on) + */ +void ssi_clock_off(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_CLOCK_OFF_SHIFT, + state << SSI_CLOCK_OFF_SHIFT, MXC_SSISOR, module); +} + +/*! + * This function enables/disables the SSI module. + * + * @param module the module number + * @param state the state for SSI module + */ +void ssi_enable(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_ENABLE_SHIFT, state << SSI_ENABLE_SHIFT, + MXC_SSISCR, module); +} + +/*! + * This function gets the data word in the Receive FIFO of the SSI module. + * + * @param module the module number + * @param fifo the Receive FIFO to read + * @return This function returns the read data. + */ +unsigned int ssi_get_data(ssi_mod module, fifo_nb fifo) +{ + unsigned int result = 0; + + if (ssi_fifo_0 == fifo) { + result = getreg_value(MXC_SSISRX0, module); + } else { + result = getreg_value(MXC_SSISRX1, module); + } + + return result; +} + +/*! + * This function returns the status of the SSI module (SISR register) as a combination of status. + * + * @param module the module number + * @return This function returns the status of the SSI module + */ +ssi_status_enable_mask ssi_get_status(ssi_mod module) +{ + unsigned int result; + + result = getreg_value(MXC_SSISISR, module); + result &= ((1 << SSI_IRQ_STATUS_NUMBER) - 1); + + return (ssi_status_enable_mask) result; +} + +/*! + * This function selects the I2S mode of the SSI module. + * + * @param module the module number + * @param mode the I2S mode + */ +void ssi_i2s_mode(ssi_mod module, mode_i2s mode) +{ + set_register_bits(3 << SSI_I2S_MODE_SHIFT, mode << SSI_I2S_MODE_SHIFT, + MXC_SSISCR, module); +} + +/*! + * This function disables the interrupts of the SSI module. + * + * @param module the module number + * @param mask the mask of the interrupts to disable + */ +void ssi_interrupt_disable(ssi_mod module, ssi_status_enable_mask mask) +{ + set_register_bits(mask, 0, MXC_SSISIER, module); +} + +/*! + * This function enables the interrupts of the SSI module. + * + * @param module the module number + * @param mask the mask of the interrupts to enable + */ +void ssi_interrupt_enable(ssi_mod module, ssi_status_enable_mask mask) +{ + set_register_bits(0, mask, MXC_SSISIER, module); +} + +/*! + * This function enables/disables the network mode of the SSI module. + * + * @param module the module number + * @param state the network mode state + */ +void ssi_network_mode(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_NETWORK_MODE_SHIFT, + state << SSI_NETWORK_MODE_SHIFT, MXC_SSISCR, module); +} + +/*! + * This function enables/disables the receive section of the SSI module. + * + * @param module the module number + * @param state the receive section state + */ +void ssi_receive_enable(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_RECEIVE_ENABLE_SHIFT, + state << SSI_RECEIVE_ENABLE_SHIFT, MXC_SSISCR, + module); +} + +/*! + * This function configures the SSI module to receive data word at bit position 0 or 23 in the Receive shift register. + * + * @param module the module number + * @param state the state to receive at bit 0 + */ +void ssi_rx_bit0(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_BIT_0_SHIFT, state << SSI_BIT_0_SHIFT, + MXC_SSISRCR, module); +} + +/*! + * This function controls the source of the clock signal used to clock the Receive shift register. + * + * @param module the module number + * @param direction the clock signal direction + */ +void ssi_rx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction) +{ + set_register_bits(1 << SSI_CLOCK_DIRECTION_SHIFT, + direction << SSI_CLOCK_DIRECTION_SHIFT, MXC_SSISRCR, + module); +} + +/*! + * This function configures the divide-by-two divider of the SSI module for the receive section. + * + * @param module the module number + * @param state the divider state + */ +void ssi_rx_clock_divide_by_two(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_DIVIDE_BY_TWO_SHIFT, + state << SSI_DIVIDE_BY_TWO_SHIFT, MXC_SSISRCCR, + module); +} + +/*! + * This function controls which bit clock edge is used to clock in data. + * + * @param module the module number + * @param polarity the clock polarity + */ +void ssi_rx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity) +{ + set_register_bits(1 << SSI_CLOCK_POLARITY_SHIFT, + polarity << SSI_CLOCK_POLARITY_SHIFT, MXC_SSISRCR, + module); +} + +/*! + * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the receive section. + * + * @param module the module number + * @param state the prescaler state + */ +void ssi_rx_clock_prescaler(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_PRESCALER_RANGE_SHIFT, + state << SSI_PRESCALER_RANGE_SHIFT, + MXC_SSISRCCR, module); +} + +/*! + * This function controls the early frame sync configuration. + * + * @param module the module number + * @param early the early frame sync configuration + */ +void ssi_rx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early) +{ + set_register_bits(1 << SSI_EARLY_FRAME_SYNC_SHIFT, + early << SSI_EARLY_FRAME_SYNC_SHIFT, + MXC_SSISRCR, module); +} + +/*! + * This function gets the number of data words in the Receive FIFO. + * + * @param module the module number + * @param fifo the fifo + * @return This function returns the number of words in the Rx FIFO. + */ +unsigned char ssi_rx_fifo_counter(ssi_mod module, fifo_nb fifo) +{ + unsigned char result; + result = 0; + + if (ssi_fifo_0 == fifo) { + result = getreg_value(MXC_SSISFCSR, module); + result &= (0xF << SSI_RX_FIFO_0_COUNT_SHIFT); + result = result >> SSI_RX_FIFO_0_COUNT_SHIFT; + } else { + result = getreg_value(MXC_SSISFCSR, module); + result &= (0xF << SSI_RX_FIFO_1_COUNT_SHIFT); + result = result >> SSI_RX_FIFO_1_COUNT_SHIFT; + } + + return result; +} + +/*! + * This function enables the Receive FIFO. + * + * @param module the module number + * @param fifo the fifo to enable + * @param enable the state of the fifo, enabled or disabled + */ + +void ssi_rx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enable) +{ + volatile unsigned int reg; + + reg = getreg_value(MXC_SSISRCR, module); + if (enable == true) { + reg |= ((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT); + } else { + reg &= ~((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT); + } + + set_register(reg, MXC_SSISRCR, module); +} + +/*! + * This function controls the threshold at which the RFFx flag will be set. + * + * @param module the module number + * @param fifo the fifo to enable + * @param watermark the watermark + * @return This function returns the result of the operation (0 if successful, -1 otherwise). + */ +int ssi_rx_fifo_full_watermark(ssi_mod module, + fifo_nb fifo, unsigned char watermark) +{ + int result = -1; + result = -1; + + if ((watermark > SSI_MIN_FIFO_WATERMARK) && + (watermark <= SSI_MAX_FIFO_WATERMARK)) { + if (ssi_fifo_0 == fifo) { + set_register_bits(0xf << SSI_RX_FIFO_0_WATERMARK_SHIFT, + watermark << + SSI_RX_FIFO_0_WATERMARK_SHIFT, + MXC_SSISFCSR, module); + } else { + set_register_bits(0xf << SSI_RX_FIFO_1_WATERMARK_SHIFT, + watermark << + SSI_RX_FIFO_1_WATERMARK_SHIFT, + MXC_SSISFCSR, module); + } + + result = 0; + } + + return result; +} + +/*! + * This function flushes the Receive FIFOs. + * + * @param module the module number + */ +void ssi_rx_flush_fifo(ssi_mod module) +{ + set_register_bits(0, 1 << SSI_RECEIVER_CLEAR_SHIFT, MXC_SSISOR, module); +} + +/*! + * This function controls the direction of the Frame Sync signal for the receive section. + * + * @param module the module number + * @param direction the Frame Sync signal direction + */ +void ssi_rx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction) +{ + set_register_bits(1 << SSI_FRAME_DIRECTION_SHIFT, + direction << SSI_FRAME_DIRECTION_SHIFT, + MXC_SSISRCR, module); +} + +/*! + * This function configures the Receive frame rate divider for the receive section. + * + * @param module the module number + * @param ratio the divide ratio from 1 to 32 + * @return This function returns the result of the operation (0 if successful, -1 otherwise). + */ +int ssi_rx_frame_rate(ssi_mod module, unsigned char ratio) +{ + int result = -1; + + if ((ratio >= SSI_MIN_FRAME_DIVIDER_RATIO) && + (ratio <= SSI_MAX_FRAME_DIVIDER_RATIO)) { + set_register_bits(SSI_FRAME_DIVIDER_MASK << + SSI_FRAME_RATE_DIVIDER_SHIFT, + (ratio - 1) << SSI_FRAME_RATE_DIVIDER_SHIFT, + MXC_SSISRCCR, module); + result = 0; + } + + return result; +} + +/*! + * This function controls the Frame Sync active polarity for the receive section. + * + * @param module the module number + * @param active the Frame Sync active polarity + */ +void ssi_rx_frame_sync_active(ssi_mod module, + ssi_tx_rx_frame_sync_active active) +{ + set_register_bits(1 << SSI_FRAME_SYNC_INVERT_SHIFT, + active << SSI_FRAME_SYNC_INVERT_SHIFT, + MXC_SSISRCR, module); +} + +/*! + * This function controls the Frame Sync length (one word or one bit long) for the receive section. + * + * @param module the module number + * @param length the Frame Sync length + */ +void ssi_rx_frame_sync_length(ssi_mod module, + ssi_tx_rx_frame_sync_length length) +{ + set_register_bits(1 << SSI_FRAME_SYNC_LENGTH_SHIFT, + length << SSI_FRAME_SYNC_LENGTH_SHIFT, + MXC_SSISRCR, module); +} + +/*! + * This function configures the time slot(s) to mask for the receive section. + * + * @param module the module number + * @param mask the mask to indicate the time slot(s) masked + */ +void ssi_rx_mask_time_slot(ssi_mod module, unsigned int mask) +{ + set_register_bits(0xFFFFFFFF, mask, MXC_SSISRMSK, module); +} + +/*! + * This function configures the Prescale divider for the receive section. + * + * @param module the module number + * @param divider the divide ratio from 1 to 256 + * @return This function returns the result of the operation (0 if successful, -1 otherwise). + */ +int ssi_rx_prescaler_modulus(ssi_mod module, unsigned int divider) +{ + int result = -1; + + if ((divider >= SSI_MIN_PRESCALER_MODULUS_RATIO) && + (divider <= SSI_MAX_PRESCALER_MODULUS_RATIO)) { + + set_register_bits(SSI_PRESCALER_MODULUS_MASK << + SSI_PRESCALER_MODULUS_SHIFT, + (divider - 1) << SSI_PRESCALER_MODULUS_SHIFT, + MXC_SSISRCCR, module); + result = 0; + } + + return result; +} + +/*! + * This function controls whether the MSB or LSB will be received first in a sample. + * + * @param module the module number + * @param direction the shift direction + */ +void ssi_rx_shift_direction(ssi_mod module, ssi_tx_rx_shift_direction direction) +{ + set_register_bits(1 << SSI_SHIFT_DIRECTION_SHIFT, + direction << SSI_SHIFT_DIRECTION_SHIFT, + MXC_SSISRCR, module); +} + +/*! + * This function configures the Receive word length. + * + * @param module the module number + * @param length the word length + */ +void ssi_rx_word_length(ssi_mod module, ssi_word_length length) +{ + set_register_bits(SSI_WORD_LENGTH_MASK << SSI_WORD_LENGTH_SHIFT, + length << SSI_WORD_LENGTH_SHIFT, + MXC_SSISRCCR, module); +} + +/*! + * This function sets the data word in the Transmit FIFO of the SSI module. + * + * @param module the module number + * @param fifo the FIFO number + * @param data the data to load in the FIFO + */ + +void ssi_set_data(ssi_mod module, fifo_nb fifo, unsigned int data) +{ + if (ssi_fifo_0 == fifo) { + set_register(data, MXC_SSISTX0, module); + } else { + set_register(data, MXC_SSISTX1, module); + } +} + +/*! + * This function controls the number of wait states between the core and SSI. + * + * @param module the module number + * @param wait the number of wait state(s) + */ +void ssi_set_wait_states(ssi_mod module, ssi_wait_states wait) +{ + set_register_bits(SSI_WAIT_STATE_MASK << SSI_WAIT_SHIFT, + wait << SSI_WAIT_SHIFT, MXC_SSISOR, module); +} + +/*! + * This function enables/disables the synchronous mode of the SSI module. + * + * @param module the module number + * @param state the synchronous mode state + */ +void ssi_synchronous_mode(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_SYNCHRONOUS_MODE_SHIFT, + state << SSI_SYNCHRONOUS_MODE_SHIFT, + MXC_SSISCR, module); +} + +/*! + * This function allows the SSI module to output the SYS_CLK at the SRCK port. + * + * @param module the module number + * @param state the system clock state + */ +void ssi_system_clock(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_SYSTEM_CLOCK_SHIFT, + state << SSI_SYSTEM_CLOCK_SHIFT, MXC_SSISCR, module); +} + +/*! + * This function enables/disables the transmit section of the SSI module. + * + * @param module the module number + * @param state the transmit section state + */ +void ssi_transmit_enable(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_TRANSMIT_ENABLE_SHIFT, + state << SSI_TRANSMIT_ENABLE_SHIFT, + MXC_SSISCR, module); +} + +/*! + * This function allows the SSI module to operate in the two channel mode. + * + * @param module the module number + * @param state the two channel mode state + */ +void ssi_two_channel_mode(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_TWO_CHANNEL_SHIFT, + state << SSI_TWO_CHANNEL_SHIFT, MXC_SSISCR, module); +} + +/*! + * This function configures the SSI module to transmit data word from bit position 0 or 23 in the Transmit shift register. + * + * @param module the module number + * @param state the transmit from bit 0 state + */ +void ssi_tx_bit0(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_BIT_0_SHIFT, + state << SSI_BIT_0_SHIFT, MXC_SSISTCR, module); +} + +/*! + * This function controls the direction of the clock signal used to clock the Transmit shift register. + * + * @param module the module number + * @param direction the clock signal direction + */ +void ssi_tx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction) +{ + set_register_bits(1 << SSI_CLOCK_DIRECTION_SHIFT, + direction << SSI_CLOCK_DIRECTION_SHIFT, + MXC_SSISTCR, module); +} + +/*! + * This function configures the divide-by-two divider of the SSI module for the transmit section. + * + * @param module the module number + * @param state the divider state + */ +void ssi_tx_clock_divide_by_two(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_DIVIDE_BY_TWO_SHIFT, + state << SSI_DIVIDE_BY_TWO_SHIFT, + MXC_SSISTCCR, module); +} + +/*! + * This function controls which bit clock edge is used to clock out data. + * + * @param module the module number + * @param polarity the clock polarity + */ +void ssi_tx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity) +{ + set_register_bits(1 << SSI_CLOCK_POLARITY_SHIFT, + polarity << SSI_CLOCK_POLARITY_SHIFT, + MXC_SSISTCR, module); +} + +/*! + * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the transmit section. + * + * @param module the module number + * @param state the prescaler state + */ +void ssi_tx_clock_prescaler(ssi_mod module, bool state) +{ + set_register_bits(1 << SSI_PRESCALER_RANGE_SHIFT, + state << SSI_PRESCALER_RANGE_SHIFT, + MXC_SSISTCCR, module); +} + +/*! + * This function controls the early frame sync configuration for the transmit section. + * + * @param module the module number + * @param early the early frame sync configuration + */ +void ssi_tx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early) +{ + set_register_bits(1 << SSI_EARLY_FRAME_SYNC_SHIFT, + early << SSI_EARLY_FRAME_SYNC_SHIFT, + MXC_SSISTCR, module); +} + +/*! + * This function gets the number of data words in the Transmit FIFO. + * + * @param module the module number + * @param fifo the fifo + * @return This function returns the number of words in the Tx FIFO. + */ +unsigned char ssi_tx_fifo_counter(ssi_mod module, fifo_nb fifo) +{ + unsigned char result = 0; + + if (ssi_fifo_0 == fifo) { + result = getreg_value(MXC_SSISFCSR, module); + result &= (0xF << SSI_TX_FIFO_0_COUNT_SHIFT); + result >>= SSI_TX_FIFO_0_COUNT_SHIFT; + } else { + result = getreg_value(MXC_SSISFCSR, module); + result &= (0xF << SSI_TX_FIFO_1_COUNT_SHIFT); + result >>= SSI_TX_FIFO_1_COUNT_SHIFT; + } + + return result; +} + +/*! + * This function controls the threshold at which the TFEx flag will be set. + * + * @param module the module number + * @param fifo the fifo to enable + * @param watermark the watermark + * @return This function returns the result of the operation (0 if successful, -1 otherwise). + */ +int ssi_tx_fifo_empty_watermark(ssi_mod module, + fifo_nb fifo, unsigned char watermark) +{ + int result = -1; + + if ((watermark > SSI_MIN_FIFO_WATERMARK) && + (watermark <= SSI_MAX_FIFO_WATERMARK)) { + if (ssi_fifo_0 == fifo) { + set_register_bits(0xf << SSI_TX_FIFO_0_WATERMARK_SHIFT, + watermark << + SSI_TX_FIFO_0_WATERMARK_SHIFT, + MXC_SSISFCSR, module); + } else { + set_register_bits(0xf << SSI_TX_FIFO_1_WATERMARK_SHIFT, + watermark << + SSI_TX_FIFO_1_WATERMARK_SHIFT, + MXC_SSISFCSR, module); + } + + result = 0; + } + + return result; +} + +/*! + * This function enables the Transmit FIFO. + * + * @param module the module number + * @param fifo the fifo to enable + * @param enable the state of the fifo, enabled or disabled + */ + +void ssi_tx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enable) +{ + unsigned int reg; + + reg = getreg_value(MXC_SSISTCR, module); + if (enable == true) { + reg |= ((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT); + } else { + reg &= ~((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT); + } + + set_register(reg, MXC_SSISTCR, module); +} + +/*! + * This function flushes the Transmit FIFOs. + * + * @param module the module number + */ +void ssi_tx_flush_fifo(ssi_mod module) +{ + set_register_bits(0, 1 << SSI_TRANSMITTER_CLEAR_SHIFT, + MXC_SSISOR, module); +} + +/*! + * This function controls the direction of the Frame Sync signal for the transmit section. + * + * @param module the module number + * @param direction the Frame Sync signal direction + */ +void ssi_tx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction) +{ + set_register_bits(1 << SSI_FRAME_DIRECTION_SHIFT, + direction << SSI_FRAME_DIRECTION_SHIFT, + MXC_SSISTCR, module); +} + +/*! + * This function configures the Transmit frame rate divider. + * + * @param module the module number + * @param ratio the divide ratio from 1 to 32 + * @return This function returns the result of the operation (0 if successful, -1 otherwise). + */ +int ssi_tx_frame_rate(ssi_mod module, unsigned char ratio) +{ + int result = -1; + + if ((ratio >= SSI_MIN_FRAME_DIVIDER_RATIO) && + (ratio <= SSI_MAX_FRAME_DIVIDER_RATIO)) { + + set_register_bits(SSI_FRAME_DIVIDER_MASK << + SSI_FRAME_RATE_DIVIDER_SHIFT, + (ratio - 1) << SSI_FRAME_RATE_DIVIDER_SHIFT, + MXC_SSISTCCR, module); + result = 0; + } + + return result; +} + +/*! + * This function controls the Frame Sync active polarity for the transmit section. + * + * @param module the module number + * @param active the Frame Sync active polarity + */ +void ssi_tx_frame_sync_active(ssi_mod module, + ssi_tx_rx_frame_sync_active active) +{ + set_register_bits(1 << SSI_FRAME_SYNC_INVERT_SHIFT, + active << SSI_FRAME_SYNC_INVERT_SHIFT, + MXC_SSISTCR, module); +} + +/*! + * This function controls the Frame Sync length (one word or one bit long) for the transmit section. + * + * @param module the module number + * @param length the Frame Sync length + */ +void ssi_tx_frame_sync_length(ssi_mod module, + ssi_tx_rx_frame_sync_length length) +{ + set_register_bits(1 << SSI_FRAME_SYNC_LENGTH_SHIFT, + length << SSI_FRAME_SYNC_LENGTH_SHIFT, + MXC_SSISTCR, module); +} + +/*! + * This function configures the time slot(s) to mask for the transmit section. + * + * @param module the module number + * @param mask the mask to indicate the time slot(s) masked + */ +void ssi_tx_mask_time_slot(ssi_mod module, unsigned int mask) +{ + set_register_bits(0xFFFFFFFF, mask, MXC_SSISTMSK, module); +} + +/*! + * This function configures the Prescale divider for the transmit section. + * + * @param module the module number + * @param divider the divide ratio from 1 to 256 + * @return This function returns the result of the operation (0 if successful, -1 otherwise). + */ +int ssi_tx_prescaler_modulus(ssi_mod module, unsigned int divider) +{ + int result = -1; + + if ((divider >= SSI_MIN_PRESCALER_MODULUS_RATIO) && + (divider <= SSI_MAX_PRESCALER_MODULUS_RATIO)) { + + set_register_bits(SSI_PRESCALER_MODULUS_MASK << + SSI_PRESCALER_MODULUS_SHIFT, + (divider - 1) << SSI_PRESCALER_MODULUS_SHIFT, + MXC_SSISTCCR, module); + result = 0; + } + + return result; +} + +/*! + * This function controls whether the MSB or LSB will be transmitted first in a sample. + * + * @param module the module number + * @param direction the shift direction + */ +void ssi_tx_shift_direction(ssi_mod module, ssi_tx_rx_shift_direction direction) +{ + set_register_bits(1 << SSI_SHIFT_DIRECTION_SHIFT, + direction << SSI_SHIFT_DIRECTION_SHIFT, + MXC_SSISTCR, module); +} + +/*! + * This function configures the Transmit word length. + * + * @param module the module number + * @param length the word length + */ +void ssi_tx_word_length(ssi_mod module, ssi_word_length length) +{ + set_register_bits(SSI_WORD_LENGTH_MASK << SSI_WORD_LENGTH_SHIFT, + length << SSI_WORD_LENGTH_SHIFT, + MXC_SSISTCCR, module); +} + +/*! + * This function initializes the driver in terms of memory of the soundcard + * and some basic HW clock settings. + * + * @return 0 on success, -1 otherwise. + */ +static int __init ssi_probe(struct platform_device *pdev) +{ + int ret = -1; + ssi_platform_data = + (struct mxc_audio_platform_data *)pdev->dev.platform_data; + if (!ssi_platform_data) { + dev_err(&pdev->dev, "can't get the platform data for SSI\n"); + return -EINVAL; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(&pdev->dev, "can't get platform resource - SSI\n"); + ret = -ENOMEM; + goto err; + } + + if (pdev->id == 0) { + base_addr_1 = res->start; + } else if (pdev->id == 1) { + base_addr_2 = res->start; + } + + printk(KERN_INFO "SSI %d module loaded successfully \n", pdev->id + 1); + + return 0; + err: + return -1; + +} + +static int ssi_remove(struct platform_device *dev) +{ + return 0; +} + +static struct platform_driver mxc_ssi_driver = { + .probe = ssi_probe, + .remove = ssi_remove, + .driver = { + .name = "mxc_ssi", + }, +}; + +/*! + * This function implements the init function of the SSI device. + * This function is called when the module is loaded. + * + * @return This function returns 0. + */ +static int __init ssi_init(void) +{ + spin_lock_init(&ssi_lock); + return platform_driver_register(&mxc_ssi_driver); + +} + +/*! + * This function implements the exit function of the SPI device. + * This function is called when the module is unloaded. + * + */ +static void __exit ssi_exit(void) +{ + platform_driver_unregister(&mxc_ssi_driver); + printk(KERN_INFO "SSI module unloaded successfully\n"); +} + +module_init(ssi_init); +module_exit(ssi_exit); + +MODULE_DESCRIPTION("SSI char device driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); |