summaryrefslogtreecommitdiff
path: root/sound/soc/ux500/ux500_msp_i2s.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/ux500/ux500_msp_i2s.c')
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c837
1 files changed, 837 insertions, 0 deletions
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
new file mode 100644
index 00000000000..71d40e16e2c
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ * Sandeep Kaushik <sandeep.kaushik@st.com>
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <mach/hardware.h>
+#include <mach/msp.h>
+
+#include <sound/soc.h>
+
+#include "ux500_msp_i2s.h"
+
+static int
+ux500_msp_i2s_enable(struct msp *msp, struct msp_config *config);
+
+ /* Protocol desciptors */
+static const struct msp_protocol_desc prot_descs[] = {
+ I2S_PROTOCOL_DESC,
+ PCM_PROTOCOL_DESC,
+ PCM_COMPAND_PROTOCOL_DESC,
+ AC97_PROTOCOL_DESC,
+ SPI_MASTER_PROTOCOL_DESC,
+ SPI_SLAVE_PROTOCOL_DESC,
+};
+
+static void ux500_msp_i2s_set_prot_desc_tx(struct msp *msp,
+ struct msp_protocol_desc *protocol_desc,
+ enum msp_data_size data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protocol_desc->tx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protocol_desc->tx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protocol_desc->tx_frame_length_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protocol_desc->tx_frame_length_2);
+ if (msp->def_elem_len) {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(protocol_desc->tx_element_length_1);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(protocol_desc->tx_element_length_2);
+ if (protocol_desc->tx_element_length_1 ==
+ protocol_desc->tx_element_length_2) {
+ msp->actual_data_size = protocol_desc->tx_element_length_1;
+ } else {
+ msp->actual_data_size = data_size;
+ }
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ msp->actual_data_size = data_size;
+ }
+ temp_reg |= MSP_DATA_DELAY_BITS(protocol_desc->tx_data_delay);
+ temp_reg |= MSP_SET_ENDIANNES_BIT(protocol_desc->tx_bit_transfer_format);
+ temp_reg |= MSP_FRAME_SYNC_POL(protocol_desc->tx_frame_sync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protocol_desc->tx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protocol_desc->compression_mode);
+ temp_reg |= MSP_SET_FRAME_SYNC_IGNORE(protocol_desc->frame_sync_ignore);
+
+ writel(temp_reg, msp->registers + MSP_TCF);
+}
+
+static void ux500_msp_i2s_set_prot_desc_rx(struct msp *msp,
+ struct msp_protocol_desc *protocol_desc,
+ enum msp_data_size data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protocol_desc->rx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protocol_desc->rx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protocol_desc->rx_frame_length_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protocol_desc->rx_frame_length_2);
+ if (msp->def_elem_len) {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(protocol_desc->rx_element_length_1);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(protocol_desc->rx_element_length_2);
+ if (protocol_desc->rx_element_length_1 ==
+ protocol_desc->rx_element_length_2) {
+ msp->actual_data_size = protocol_desc->rx_element_length_1;
+ } else {
+ msp->actual_data_size = data_size;
+ }
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ msp->actual_data_size = data_size;
+ }
+
+ temp_reg |= MSP_DATA_DELAY_BITS(protocol_desc->rx_data_delay);
+ temp_reg |= MSP_SET_ENDIANNES_BIT(protocol_desc->rx_bit_transfer_format);
+ temp_reg |= MSP_FRAME_SYNC_POL(protocol_desc->rx_frame_sync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protocol_desc->rx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protocol_desc->expansion_mode);
+ temp_reg |= MSP_SET_FRAME_SYNC_IGNORE(protocol_desc->frame_sync_ignore);
+
+ writel(temp_reg, msp->registers + MSP_RCF);
+
+}
+
+static int ux500_msp_i2s_configure_protocol(struct msp *msp,
+ struct msp_config *config)
+{
+ int direction;
+ struct msp_protocol_desc *protocol_desc;
+ enum msp_data_size data_size;
+ u32 temp_reg = 0;
+
+ data_size = config->data_size;
+ msp->def_elem_len = config->def_elem_len;
+ direction = config->direction;
+ if (config->default_protocol_desc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ pr_err("%s: ERROR: Invalid protocol!\n", __func__);
+ return -EINVAL;
+ }
+ protocol_desc =
+ (struct msp_protocol_desc *)&prot_descs[config->protocol];
+ } else {
+ protocol_desc = (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+
+ if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) {
+ pr_err("%s: ERROR: Invalid data-size requested (data_size = %d)!\n",
+ __func__, data_size);
+ return -EINVAL;
+ }
+
+ switch (direction) {
+ case MSP_TRANSMIT_MODE:
+ ux500_msp_i2s_set_prot_desc_tx(msp, protocol_desc, data_size);
+ break;
+ case MSP_RECEIVE_MODE:
+ ux500_msp_i2s_set_prot_desc_rx(msp, protocol_desc, data_size);
+ break;
+ case MSP_BOTH_T_R_MODE:
+ ux500_msp_i2s_set_prot_desc_tx(msp, protocol_desc, data_size);
+ ux500_msp_i2s_set_prot_desc_rx(msp, protocol_desc, data_size);
+ break;
+ default:
+ pr_err("%s: ERROR: Invalid direction requested (direction = %d)!\n",
+ __func__, direction);
+ return -EINVAL;
+ }
+
+ /* The below code is needed for both Rx and Tx path. Can't separate them. */
+ temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
+ temp_reg |= MSP_TX_CLKPOL_BIT(~protocol_desc->tx_clock_pol);
+ writel(temp_reg, msp->registers + MSP_GCR);
+ temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING;
+ temp_reg |= MSP_RX_CLKPOL_BIT(protocol_desc->rx_clock_pol);
+ writel(temp_reg, msp->registers + MSP_GCR);
+
+ return 0;
+}
+
+static int ux500_msp_i2s_configure_clock(struct msp *msp, struct msp_config *config)
+{
+ u32 reg_val_GCR;
+ u32 frame_per = 0;
+ u32 sck_div = 0;
+ u32 frame_width = 0;
+ u32 temp_reg = 0;
+ u32 bit_clock = 0;
+ struct msp_protocol_desc *protocol_desc = NULL;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR);
+
+ if (config->default_protocol_desc)
+ protocol_desc =
+ (struct msp_protocol_desc *)&prot_descs[config->protocol];
+ else
+ protocol_desc = (struct msp_protocol_desc *)&config->protocol_desc;
+
+ switch (config->protocol) {
+ case MSP_PCM_PROTOCOL:
+ case MSP_PCM_COMPAND_PROTOCOL:
+ frame_width = protocol_desc->frame_width;
+ sck_div = config->input_clock_freq / (config->frame_freq *
+ (protocol_desc->total_clocks_for_one_frame));
+ frame_per = protocol_desc->frame_period;
+ break;
+ case MSP_I2S_PROTOCOL:
+ frame_width = protocol_desc->frame_width;
+ sck_div = config->input_clock_freq / (config->frame_freq *
+ (protocol_desc->total_clocks_for_one_frame));
+ frame_per = protocol_desc->frame_period;
+ break;
+ case MSP_AC97_PROTOCOL:
+ /* Not supported */
+ pr_err("%s: ERROR: AC97 protocol not supported!\n", __func__);
+ return -ENOSYS;
+ default:
+ pr_err("%s: ERROR: Unknown protocol (%d)!\n",
+ __func__,
+ config->protocol);
+ return -EINVAL;
+ }
+
+ temp_reg = (sck_div - 1) & SCK_DIV_MASK;
+ temp_reg |= FRAME_WIDTH_BITS(frame_width);
+ temp_reg |= FRAME_PERIOD_BITS(frame_per);
+ writel(temp_reg, msp->registers + MSP_SRG);
+
+ bit_clock = (config->input_clock_freq)/(sck_div + 1);
+ /* If the bit clock is higher than 19.2MHz, Vape should be run in 100% OPP
+ * Only consider OPP 100% when bit-clock is used, i.e. MSP master mode
+ */
+ if ((bit_clock > 19200000) && ((config->tx_clock_sel != 0) || (config->rx_clock_sel != 0))) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500-msp-i2s", 100);
+ msp->vape_opp_constraint = 1;
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500-msp-i2s", 50);
+ msp->vape_opp_constraint = 0;
+ }
+
+ /* Enable clock */
+ udelay(100);
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | SRG_ENABLE, msp->registers + MSP_GCR);
+ udelay(100);
+
+ return 0;
+}
+
+static int ux500_msp_i2s_configure_multichannel(struct msp *msp, struct msp_config *config)
+{
+ struct msp_protocol_desc *protocol_desc;
+ struct msp_multichannel_config *mcfg;
+ u32 reg_val_MCR;
+
+ if (config->default_protocol_desc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ pr_err("%s: ERROR: Invalid protocol (%d)!\n",
+ __func__,
+ config->protocol);
+ return -EINVAL;
+ }
+ protocol_desc = (struct msp_protocol_desc *)
+ &prot_descs[config->protocol];
+ } else {
+ protocol_desc = (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+
+ mcfg = &config->multichannel_config;
+ if (mcfg->tx_multichannel_enable) {
+ if (protocol_desc->tx_phase_mode == MSP_SINGLE_PHASE) {
+ reg_val_MCR = readl(msp->registers + MSP_MCR);
+ writel(reg_val_MCR |
+ (mcfg->tx_multichannel_enable ? 1 << TMCEN_BIT : 0),
+ msp->registers + MSP_MCR);
+ writel(mcfg->tx_channel_0_enable,
+ msp->registers + MSP_TCE0);
+ writel(mcfg->tx_channel_1_enable,
+ msp->registers + MSP_TCE1);
+ writel(mcfg->tx_channel_2_enable,
+ msp->registers + MSP_TCE2);
+ writel(mcfg->tx_channel_3_enable,
+ msp->registers + MSP_TCE3);
+ } else {
+ pr_err("%s: ERROR: Only single-phase supported (TX-mode: %d)!\n",
+ __func__, protocol_desc->tx_phase_mode);
+ return -EINVAL;
+ }
+ }
+ if (mcfg->rx_multichannel_enable) {
+ if (protocol_desc->rx_phase_mode == MSP_SINGLE_PHASE) {
+ reg_val_MCR = readl(msp->registers + MSP_MCR);
+ writel(reg_val_MCR |
+ (mcfg->rx_multichannel_enable ? 1 << RMCEN_BIT : 0),
+ msp->registers + MSP_MCR);
+ writel(mcfg->rx_channel_0_enable,
+ msp->registers + MSP_RCE0);
+ writel(mcfg->rx_channel_1_enable,
+ msp->registers + MSP_RCE1);
+ writel(mcfg->rx_channel_2_enable,
+ msp->registers + MSP_RCE2);
+ writel(mcfg->rx_channel_3_enable,
+ msp->registers + MSP_RCE3);
+ } else {
+ pr_err("%s: ERROR: Only single-phase supported (RX-mode: %d)!\n",
+ __func__, protocol_desc->rx_phase_mode);
+ return -EINVAL;
+ }
+ if (mcfg->rx_comparison_enable_mode) {
+ reg_val_MCR = readl(msp->registers + MSP_MCR);
+ writel(reg_val_MCR |
+ (mcfg->rx_comparison_enable_mode << RCMPM_BIT),
+ msp->registers + MSP_MCR);
+
+ writel(mcfg->comparison_mask,
+ msp->registers + MSP_RCM);
+ writel(mcfg->comparison_value,
+ msp->registers + MSP_RCV);
+
+ }
+ }
+
+ return 0;
+}
+
+static int ux500_msp_i2s_enable(struct msp *msp, struct msp_config *config)
+{
+ int status = 0;
+ u32 reg_val_DMACR, reg_val_GCR;
+
+ if (config->work_mode != MSP_DMA_MODE) {
+ pr_err("%s: ERROR: Only DMA-mode is supported (msp->work_mode = %d)\n",
+ __func__,
+ msp->work_mode);
+ return -EINVAL;
+ }
+ msp->work_mode = config->work_mode;
+
+ /* Check msp state whether in RUN or CONFIGURED Mode */
+ if (msp->msp_state == MSP_STATE_IDLE) {
+ if (msp->plat_init) {
+ status = msp->plat_init();
+ if (status) {
+ pr_err("%s: ERROR: Failed to init MSP (%d)!\n",
+ __func__,
+ status);
+ return status;
+ }
+ }
+ }
+
+ /* Configure msp with protocol dependent settings */
+ ux500_msp_i2s_configure_protocol(msp, config);
+ ux500_msp_i2s_configure_clock(msp, config);
+ if (config->multichannel_configured == 1) {
+ status = ux500_msp_i2s_configure_multichannel(msp, config);
+ if (status)
+ pr_warn("%s: WARN: ux500_msp_i2s_configure_multichannel failed (%d)!\n",
+ __func__, status);
+ }
+
+ /* Make sure the correct DMA-directions are configured */
+ if ((config->direction == MSP_RECEIVE_MODE) ||
+ (config->direction == MSP_BOTH_T_R_MODE))
+ if (!msp->dma_cfg_rx) {
+ pr_err("%s: ERROR: MSP RX-mode is not configured!", __func__);
+ return -EINVAL;
+ }
+ if ((config->direction == MSP_TRANSMIT_MODE) ||
+ (config->direction == MSP_BOTH_T_R_MODE))
+ if (!msp->dma_cfg_tx) {
+ pr_err("%s: ERROR: MSP TX-mode is not configured!", __func__);
+ return -EINVAL;
+ }
+
+ reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+ switch (config->direction) {
+ case MSP_TRANSMIT_MODE:
+ writel(reg_val_DMACR | TX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ break;
+ case MSP_RECEIVE_MODE:
+ writel(reg_val_DMACR | RX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+ break;
+ case MSP_BOTH_T_R_MODE:
+ writel(reg_val_DMACR | RX_DMA_ENABLE | TX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+ break;
+ default:
+ pr_err("%s: ERROR: Illegal MSP direction (config->direction = %d)!",
+ __func__,
+ config->direction);
+ if (msp->plat_exit)
+ msp->plat_exit();
+ return -EINVAL;
+ }
+ writel(config->iodelay, msp->registers + MSP_IODLY);
+
+ /* Enable frame generation logic */
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR);
+
+ return status;
+}
+
+static void flush_fifo_rx(struct msp *msp)
+{
+ u32 reg_val_DR, reg_val_GCR, reg_val_FLR;
+ u32 limit = 32;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | RX_ENABLE, msp->registers + MSP_GCR);
+
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) {
+ reg_val_DR = readl(msp->registers + MSP_DR);
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ }
+
+ writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+static void flush_fifo_tx(struct msp *msp)
+{
+ u32 reg_val_TSTDR, reg_val_GCR, reg_val_FLR;
+ u32 limit = 32;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | TX_ENABLE, msp->registers + MSP_GCR);
+ writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, msp->registers + MSP_ITCR);
+
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) {
+ reg_val_TSTDR = readl(msp->registers + MSP_TSTDR);
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ }
+ writel(0x0, msp->registers + MSP_ITCR);
+ writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+int ux500_msp_i2s_open(struct ux500_msp_i2s_drvdata *drvdata, struct msp_config *msp_config)
+{
+ struct msp *msp = drvdata->msp;
+ u32 old_reg, new_reg, mask;
+ int res;
+
+ if (in_interrupt()) {
+ pr_err("%s: ERROR: Open called in interrupt context!\n", __func__);
+ return -1;
+ }
+
+ /* Two simultanous configuring msp is avoidable */
+ down(&msp->lock);
+
+ /* Don't enable regulator if its msp1 or msp3 */
+ if (!(msp->reg_enabled) && msp->id != MSP_1_I2S_CONTROLLER
+ && msp->id != MSP_3_I2S_CONTROLLER) {
+ res = regulator_enable(drvdata->reg_vape);
+ if (res != 0) {
+ pr_err("%s: Failed to enable regulator!\n", __func__);
+ up(&msp->lock);
+ return res;
+ }
+ msp->reg_enabled = 1;
+ }
+
+ switch (msp->users) {
+ case 0:
+ clk_enable(msp->clk);
+ msp->direction = msp_config->direction;
+ break;
+ case 1:
+ if (msp->direction == MSP_BOTH_T_R_MODE ||
+ msp_config->direction == msp->direction ||
+ msp_config->direction == MSP_BOTH_T_R_MODE) {
+ pr_warn("%s: WARN: MSP is in use (direction = %d)!\n",
+ __func__, msp_config->direction);
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ msp->direction = MSP_BOTH_T_R_MODE;
+ break;
+ default:
+ pr_warn("%s: MSP in use in (both directions)!\n", __func__);
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ msp->users++;
+
+ /* First do the global config register */
+ mask =
+ RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FRAME_SYNC_MASK |
+ TX_FRAME_SYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
+ RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK |
+ LOOPBACK_MASK | TX_EXTRA_DELAY_MASK;
+
+ new_reg = (msp_config->tx_clock_sel | msp_config->rx_clock_sel |
+ msp_config->rx_frame_sync_pol | msp_config->tx_frame_sync_pol |
+ msp_config->rx_frame_sync_sel | msp_config->tx_frame_sync_sel |
+ msp_config->rx_fifo_config | msp_config->tx_fifo_config |
+ msp_config->srg_clock_sel | msp_config->loopback_enable |
+ msp_config->tx_data_enable);
+
+ old_reg = readl(msp->registers + MSP_GCR);
+ old_reg &= ~mask;
+ new_reg |= old_reg;
+ writel(new_reg, msp->registers + MSP_GCR);
+
+ if (ux500_msp_i2s_enable(msp, msp_config) != 0) {
+ pr_err("%s: ERROR: ux500_msp_i2s_enable failed!\n", __func__);
+ return -EBUSY;
+ }
+ if (msp_config->loopback_enable & 0x80)
+ msp->loopback_enable = 1;
+
+ /* Flush FIFOs */
+ flush_fifo_tx(msp);
+ flush_fifo_rx(msp);
+
+ msp->msp_state = MSP_STATE_CONFIGURED;
+ up(&msp->lock);
+ return 0;
+}
+
+static void ux500_msp_i2s_disable_rx(struct msp *msp)
+{
+ u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR & ~RX_ENABLE, msp->registers + MSP_GCR);
+ reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+ writel(reg_val_DMACR & ~RX_DMA_ENABLE, msp->registers + MSP_DMACR);
+ reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+ writel(reg_val_IMSC &
+ ~(RECEIVE_SERVICE_INT | RECEIVE_OVERRUN_ERROR_INT),
+ msp->registers + MSP_IMSC);
+}
+
+static void ux500_msp_i2s_disable_tx(struct msp *msp)
+{
+ u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR & ~TX_ENABLE, msp->registers + MSP_GCR);
+ reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+ writel(reg_val_DMACR & ~TX_DMA_ENABLE, msp->registers + MSP_DMACR);
+ reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+ writel(reg_val_IMSC &
+ ~(TRANSMIT_SERVICE_INT | TRANSMIT_UNDERRUN_ERR_INT),
+ msp->registers + MSP_IMSC);
+}
+
+static int ux500_msp_i2s_disable(struct msp *msp, int direction, enum i2s_flag flag)
+{
+ u32 reg_val_GCR;
+ int status = 0;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+
+ if (flag == DISABLE_TRANSMIT)
+ ux500_msp_i2s_disable_tx(msp);
+ else if (flag == DISABLE_RECEIVE)
+ ux500_msp_i2s_disable_rx(msp);
+ else {
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | LOOPBACK_MASK,
+ msp->registers + MSP_GCR);
+
+ /* Flush TX-FIFO */
+ flush_fifo_tx(msp);
+
+ /* Disable TX-channel */
+ writel((readl(msp->registers + MSP_GCR) &
+ (~TX_ENABLE)), msp->registers + MSP_GCR);
+
+ /* Flush RX-FIFO */
+ flush_fifo_rx(msp);
+
+ /* Disable Loopback and Receive channel */
+ writel((readl(msp->registers + MSP_GCR) &
+ (~(RX_ENABLE | LOOPBACK_MASK))),
+ msp->registers + MSP_GCR);
+
+ ux500_msp_i2s_disable_tx(msp);
+ ux500_msp_i2s_disable_rx(msp);
+
+ }
+
+ /* disable sample rate and frame generators */
+ if (flag == DISABLE_ALL) {
+ msp->msp_state = MSP_STATE_IDLE;
+ writel((readl(msp->registers + MSP_GCR) &
+ (~(FRAME_GEN_ENABLE | SRG_ENABLE))),
+ msp->registers + MSP_GCR);
+ if (msp->plat_exit)
+ status = msp->plat_exit();
+ if (status)
+ pr_warn("%s: WARN: ux500_msp_i2s_exit failed (%d)!\n",
+ __func__, status);
+ writel(0, msp->registers + MSP_GCR);
+ writel(0, msp->registers + MSP_TCF);
+ writel(0, msp->registers + MSP_RCF);
+ writel(0, msp->registers + MSP_DMACR);
+ writel(0, msp->registers + MSP_SRG);
+ writel(0, msp->registers + MSP_MCR);
+ writel(0, msp->registers + MSP_RCM);
+ writel(0, msp->registers + MSP_RCV);
+ writel(0, msp->registers + MSP_TCE0);
+ writel(0, msp->registers + MSP_TCE1);
+ writel(0, msp->registers + MSP_TCE2);
+ writel(0, msp->registers + MSP_TCE3);
+ writel(0, msp->registers + MSP_RCE0);
+ writel(0, msp->registers + MSP_RCE1);
+ writel(0, msp->registers + MSP_RCE2);
+ writel(0, msp->registers + MSP_RCE3);
+ }
+
+ return status;
+}
+
+int
+ux500_msp_i2s_trigger(
+ struct ux500_msp_i2s_drvdata *drvdata,
+ int cmd,
+ int direction)
+{
+ struct msp *msp = drvdata->msp;
+ u32 reg_val_GCR, enable_bit;
+
+ if (msp->msp_state == MSP_STATE_IDLE) {
+ pr_err("%s: ERROR: MSP is not configured!\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ enable_bit = TX_ENABLE;
+ } else {
+ enable_bit = RX_ENABLE;
+ }
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ ux500_msp_i2s_disable_tx(msp);
+ } else {
+ ux500_msp_i2s_disable_rx(msp);
+ }
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+
+int ux500_msp_i2s_close(struct ux500_msp_i2s_drvdata *drvdata, enum i2s_flag flag)
+{
+ struct msp *msp = drvdata->msp;
+ int status = 0;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ down(&msp->lock);
+
+ if (msp->users == 0) {
+ pr_err("%s: ERROR: MSP already closed!\n", __func__);
+ status = -EINVAL;
+ goto end;
+ }
+ pr_debug("%s: msp->users = %d, flag = %d\n", __func__, msp->users, flag);
+
+ /* We need to call it twice for DISABLE_ALL*/
+ msp->users = flag == DISABLE_ALL ? 0 : msp->users - 1;
+ if (msp->users)
+ status = ux500_msp_i2s_disable(msp, MSP_BOTH_T_R_MODE, flag);
+ else {
+ status = ux500_msp_i2s_disable(msp, MSP_BOTH_T_R_MODE, DISABLE_ALL);
+ clk_disable(msp->clk);
+ if (msp->reg_enabled) {
+ status = regulator_disable(drvdata->reg_vape);
+ msp->reg_enabled = 0;
+ }
+ if (status != 0) {
+ pr_err("%s: ERROR: Failed to disable regulator (%d)!\n",
+ __func__, status);
+ clk_enable(msp->clk);
+ goto end;
+ }
+ }
+ if (status)
+ goto end;
+ if (msp->users)
+ msp->direction = flag == DISABLE_TRANSMIT ?
+ MSP_RECEIVE_MODE : MSP_TRANSMIT_MODE;
+
+ if (msp->vape_opp_constraint == 1) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s", 50);
+ msp->vape_opp_constraint = 0;
+ }
+end:
+ up(&msp->lock);
+ return status;
+
+}
+
+struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev,
+ struct msp_i2s_platform_data *platform_data)
+{
+ struct ux500_msp_i2s_drvdata *msp_i2s_drvdata;
+ int irq;
+ struct resource *res = NULL;
+ struct i2s_controller *i2s_cont;
+ struct msp *msp;
+
+ pr_debug("%s: Enter (pdev->name = %s).\n", __func__, pdev->name);
+
+ msp_i2s_drvdata = kzalloc(sizeof(struct ux500_msp_i2s_drvdata), GFP_KERNEL);
+ msp_i2s_drvdata->msp = kzalloc(sizeof(struct msp), GFP_KERNEL);
+ msp = msp_i2s_drvdata->msp;
+
+ msp->id = platform_data->id;
+ msp_i2s_drvdata->id = msp->id;
+ pr_debug("msp_i2s_drvdata->id = %d\n", msp_i2s_drvdata->id);
+
+ msp->plat_init = platform_data->msp_i2s_init;
+ msp->plat_exit = platform_data->msp_i2s_exit;
+ msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;
+ msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx;
+
+ sema_init(&msp->lock, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ pr_err("%s: ERROR: Unable to get resource!\n", __func__);
+ goto free_msp;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ goto free_msp;
+ msp->irq = irq;
+
+ msp->registers = ioremap(res->start, (res->end - res->start + 1));
+ if (msp->registers == NULL)
+ goto free_msp;
+
+ msp_i2s_drvdata->reg_vape = regulator_get(NULL, "v-ape");
+ if (IS_ERR(msp_i2s_drvdata->reg_vape)) {
+ pr_err("%s: ERROR: Failed to get Vape supply (%d)!\n",
+ __func__, (int)PTR_ERR(msp_i2s_drvdata->reg_vape));
+ goto free_irq;
+ }
+ dev_set_drvdata(&pdev->dev, msp_i2s_drvdata);
+
+ prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name,
+ PRCMU_QOS_DEFAULT_VALUE);
+ msp->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(msp->clk)) {
+ pr_err("%s: ERROR: clk_get failed (%d)!\n",
+ __func__, (int)PTR_ERR(msp->clk));
+ goto free_irq;
+ }
+
+ msp->msp_state = MSP_STATE_IDLE;
+ msp->loopback_enable = 0;
+
+ /* I2S Controller is allocated and added in I2S controller class. */
+ i2s_cont = kzalloc(sizeof(*i2s_cont), GFP_KERNEL);
+ if (!i2s_cont) {
+ pr_err("%s: ERROR: Failed to allocate struct i2s_cont (kzalloc)!\n",
+ __func__);
+ goto del_clk;
+ }
+ i2s_cont->dev.parent = &pdev->dev;
+ i2s_cont->data = (void *)msp;
+ i2s_cont->id = (s16)msp->id;
+ snprintf(i2s_cont->name,
+ sizeof(i2s_cont->name),
+ "ux500-msp-i2s.%04x",
+ msp->id);
+ pr_debug("I2S device-name :%s\n", i2s_cont->name);
+ msp->i2s_cont = i2s_cont;
+
+ return msp_i2s_drvdata;
+
+del_clk:
+ clk_put(msp->clk);
+free_irq:
+ iounmap(msp->registers);
+free_msp:
+ kfree(msp);
+ return NULL;
+}
+
+int ux500_msp_i2s_exit(struct ux500_msp_i2s_drvdata *drvdata)
+{
+ struct msp *msp = drvdata->msp;
+ int status = 0;
+
+ pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id);
+
+ device_unregister(&msp->i2s_cont->dev);
+ clk_put(msp->clk);
+ iounmap(msp->registers);
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
+ regulator_put(drvdata->reg_vape);
+ kfree(msp);
+
+ return status;
+}
+
+int ux500_msp_i2s_suspend(struct ux500_msp_i2s_drvdata *drvdata)
+{
+ struct msp *msp = drvdata->msp;
+
+ pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id);
+
+ down(&msp->lock);
+ if (msp->users > 0) {
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ up(&msp->lock);
+
+ return 0;
+}
+
+int ux500_msp_i2s_resume(struct ux500_msp_i2s_drvdata *drvdata)
+{
+ pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id);
+
+ return 0;
+}
+
+MODULE_LICENSE("GPLv2");