linux/sound/soc/ux500/ux500_msp_i2s.c
Lee Jones 05c56c2413 ASoC: ux500_pcm: Extend Device Tree support to deal with DMA data
Soon we will strip out pdata support from the Ux500 set of ASoC drivers.
When this happens it will have to supply a DMA slave_config to the
dmaengine. At the moment a great deal of this comes from pdata via
AUXDATA. We need to become independent of this soon. This patch starts
the process by allocating memory for the associated data structures and
fetches the MSP id used for const struct indexing.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Mark Brown <broonie@linaro.org>
2014-01-07 15:37:20 +00:00

737 lines
20 KiB
C

/*
* Copyright (C) ST-Ericsson SA 2012
*
* 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/delay.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/platform_data/asoc-ux500-msp.h>
#include <sound/soc.h>
#include "ux500_msp_i2s.h"
/* Protocol desciptors */
static const struct msp_protdesc prot_descs[] = {
{ /* I2S */
MSP_SINGLE_PHASE,
MSP_SINGLE_PHASE,
MSP_PHASE2_START_MODE_IMEDIATE,
MSP_PHASE2_START_MODE_IMEDIATE,
MSP_BTF_MS_BIT_FIRST,
MSP_BTF_MS_BIT_FIRST,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_ELEM_LEN_32,
MSP_ELEM_LEN_32,
MSP_ELEM_LEN_32,
MSP_ELEM_LEN_32,
MSP_DELAY_1,
MSP_DELAY_1,
MSP_RISING_EDGE,
MSP_FALLING_EDGE,
MSP_FSYNC_POL_ACT_LO,
MSP_FSYNC_POL_ACT_LO,
MSP_SWAP_NONE,
MSP_SWAP_NONE,
MSP_COMPRESS_MODE_LINEAR,
MSP_EXPAND_MODE_LINEAR,
MSP_FSYNC_IGNORE,
31,
15,
32,
}, { /* PCM */
MSP_DUAL_PHASE,
MSP_DUAL_PHASE,
MSP_PHASE2_START_MODE_FSYNC,
MSP_PHASE2_START_MODE_FSYNC,
MSP_BTF_MS_BIT_FIRST,
MSP_BTF_MS_BIT_FIRST,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_ELEM_LEN_16,
MSP_ELEM_LEN_16,
MSP_ELEM_LEN_16,
MSP_ELEM_LEN_16,
MSP_DELAY_0,
MSP_DELAY_0,
MSP_RISING_EDGE,
MSP_FALLING_EDGE,
MSP_FSYNC_POL_ACT_HI,
MSP_FSYNC_POL_ACT_HI,
MSP_SWAP_NONE,
MSP_SWAP_NONE,
MSP_COMPRESS_MODE_LINEAR,
MSP_EXPAND_MODE_LINEAR,
MSP_FSYNC_IGNORE,
255,
0,
256,
}, { /* Companded PCM */
MSP_SINGLE_PHASE,
MSP_SINGLE_PHASE,
MSP_PHASE2_START_MODE_FSYNC,
MSP_PHASE2_START_MODE_FSYNC,
MSP_BTF_MS_BIT_FIRST,
MSP_BTF_MS_BIT_FIRST,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_ELEM_LEN_8,
MSP_ELEM_LEN_8,
MSP_ELEM_LEN_8,
MSP_ELEM_LEN_8,
MSP_DELAY_0,
MSP_DELAY_0,
MSP_RISING_EDGE,
MSP_RISING_EDGE,
MSP_FSYNC_POL_ACT_HI,
MSP_FSYNC_POL_ACT_HI,
MSP_SWAP_NONE,
MSP_SWAP_NONE,
MSP_COMPRESS_MODE_LINEAR,
MSP_EXPAND_MODE_LINEAR,
MSP_FSYNC_IGNORE,
255,
0,
256,
},
};
static void set_prot_desc_tx(struct ux500_msp *msp,
struct msp_protdesc *protdesc,
enum msp_data_size data_size)
{
u32 temp_reg = 0;
temp_reg |= MSP_P2_ENABLE_BIT(protdesc->tx_phase_mode);
temp_reg |= MSP_P2_START_MODE_BIT(protdesc->tx_phase2_start_mode);
temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->tx_frame_len_1);
temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->tx_frame_len_2);
if (msp->def_elem_len) {
temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->tx_elem_len_1);
temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->tx_elem_len_2);
} else {
temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
}
temp_reg |= MSP_DATA_DELAY_BITS(protdesc->tx_data_delay);
temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->tx_byte_order);
temp_reg |= MSP_FSYNC_POL(protdesc->tx_fsync_pol);
temp_reg |= MSP_DATA_WORD_SWAP(protdesc->tx_half_word_swap);
temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->compression_mode);
temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
writel(temp_reg, msp->registers + MSP_TCF);
}
static void set_prot_desc_rx(struct ux500_msp *msp,
struct msp_protdesc *protdesc,
enum msp_data_size data_size)
{
u32 temp_reg = 0;
temp_reg |= MSP_P2_ENABLE_BIT(protdesc->rx_phase_mode);
temp_reg |= MSP_P2_START_MODE_BIT(protdesc->rx_phase2_start_mode);
temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->rx_frame_len_1);
temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->rx_frame_len_2);
if (msp->def_elem_len) {
temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->rx_elem_len_1);
temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->rx_elem_len_2);
} else {
temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
}
temp_reg |= MSP_DATA_DELAY_BITS(protdesc->rx_data_delay);
temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->rx_byte_order);
temp_reg |= MSP_FSYNC_POL(protdesc->rx_fsync_pol);
temp_reg |= MSP_DATA_WORD_SWAP(protdesc->rx_half_word_swap);
temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->expansion_mode);
temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
writel(temp_reg, msp->registers + MSP_RCF);
}
static int configure_protocol(struct ux500_msp *msp,
struct ux500_msp_config *config)
{
struct msp_protdesc *protdesc;
enum msp_data_size data_size;
u32 temp_reg = 0;
data_size = config->data_size;
msp->def_elem_len = config->def_elem_len;
if (config->default_protdesc == 1) {
if (config->protocol >= MSP_INVALID_PROTOCOL) {
dev_err(msp->dev, "%s: ERROR: Invalid protocol!\n",
__func__);
return -EINVAL;
}
protdesc =
(struct msp_protdesc *)&prot_descs[config->protocol];
} else {
protdesc = (struct msp_protdesc *)&config->protdesc;
}
if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) {
dev_err(msp->dev,
"%s: ERROR: Invalid data-size requested (data_size = %d)!\n",
__func__, data_size);
return -EINVAL;
}
if (config->direction & MSP_DIR_TX)
set_prot_desc_tx(msp, protdesc, data_size);
if (config->direction & MSP_DIR_RX)
set_prot_desc_rx(msp, protdesc, data_size);
/* The code below should not be separated. */
temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_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(protdesc->rx_clk_pol);
writel(temp_reg, msp->registers + MSP_GCR);
return 0;
}
static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config)
{
u32 reg_val_GCR;
u32 frame_per = 0;
u32 sck_div = 0;
u32 frame_width = 0;
u32 temp_reg = 0;
struct msp_protdesc *protdesc = NULL;
reg_val_GCR = readl(msp->registers + MSP_GCR);
writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR);
if (config->default_protdesc)
protdesc =
(struct msp_protdesc *)&prot_descs[config->protocol];
else
protdesc = (struct msp_protdesc *)&config->protdesc;
switch (config->protocol) {
case MSP_PCM_PROTOCOL:
case MSP_PCM_COMPAND_PROTOCOL:
frame_width = protdesc->frame_width;
sck_div = config->f_inputclk / (config->frame_freq *
(protdesc->clocks_per_frame));
frame_per = protdesc->frame_period;
break;
case MSP_I2S_PROTOCOL:
frame_width = protdesc->frame_width;
sck_div = config->f_inputclk / (config->frame_freq *
(protdesc->clocks_per_frame));
frame_per = protdesc->frame_period;
break;
default:
dev_err(msp->dev, "%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);
msp->f_bitclk = (config->f_inputclk)/(sck_div + 1);
/* Enable bit-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 configure_multichannel(struct ux500_msp *msp,
struct ux500_msp_config *config)
{
struct msp_protdesc *protdesc;
struct msp_multichannel_config *mcfg;
u32 reg_val_MCR;
if (config->default_protdesc == 1) {
if (config->protocol >= MSP_INVALID_PROTOCOL) {
dev_err(msp->dev,
"%s: ERROR: Invalid protocol (%d)!\n",
__func__, config->protocol);
return -EINVAL;
}
protdesc = (struct msp_protdesc *)
&prot_descs[config->protocol];
} else {
protdesc = (struct msp_protdesc *)&config->protdesc;
}
mcfg = &config->multichannel_config;
if (mcfg->tx_multichannel_enable) {
if (protdesc->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 {
dev_err(msp->dev,
"%s: ERROR: Only single-phase supported (TX-mode: %d)!\n",
__func__, protdesc->tx_phase_mode);
return -EINVAL;
}
}
if (mcfg->rx_multichannel_enable) {
if (protdesc->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 {
dev_err(msp->dev,
"%s: ERROR: Only single-phase supported (RX-mode: %d)!\n",
__func__, protdesc->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 enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
{
int status = 0;
u32 reg_val_DMACR, reg_val_GCR;
/* Configure msp with protocol dependent settings */
configure_protocol(msp, config);
setup_bitclk(msp, config);
if (config->multichannel_configured == 1) {
status = configure_multichannel(msp, config);
if (status)
dev_warn(msp->dev,
"%s: WARN: configure_multichannel failed (%d)!\n",
__func__, status);
}
/* Make sure the correct DMA-directions are configured */
if ((config->direction & MSP_DIR_RX) &&
!msp->capture_dma_data.dma_cfg) {
dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!",
__func__);
return -EINVAL;
}
if ((config->direction == MSP_DIR_TX) &&
!msp->playback_dma_data.dma_cfg) {
dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!",
__func__);
return -EINVAL;
}
reg_val_DMACR = readl(msp->registers + MSP_DMACR);
if (config->direction & MSP_DIR_RX)
reg_val_DMACR |= RX_DMA_ENABLE;
if (config->direction & MSP_DIR_TX)
reg_val_DMACR |= TX_DMA_ENABLE;
writel(reg_val_DMACR, msp->registers + MSP_DMACR);
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 ux500_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 ux500_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 *msp,
struct ux500_msp_config *config)
{
u32 old_reg, new_reg, mask;
int res;
unsigned int tx_sel, rx_sel, tx_busy, rx_busy;
if (in_interrupt()) {
dev_err(msp->dev,
"%s: ERROR: Open called in interrupt context!\n",
__func__);
return -1;
}
tx_sel = (config->direction & MSP_DIR_TX) > 0;
rx_sel = (config->direction & MSP_DIR_RX) > 0;
if (!tx_sel && !rx_sel) {
dev_err(msp->dev, "%s: Error: No direction selected!\n",
__func__);
return -EINVAL;
}
tx_busy = (msp->dir_busy & MSP_DIR_TX) > 0;
rx_busy = (msp->dir_busy & MSP_DIR_RX) > 0;
if (tx_busy && tx_sel) {
dev_err(msp->dev, "%s: Error: TX is in use!\n", __func__);
return -EBUSY;
}
if (rx_busy && rx_sel) {
dev_err(msp->dev, "%s: Error: RX is in use!\n", __func__);
return -EBUSY;
}
msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0);
/* First do the global config register */
mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK |
TX_FSYNC_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 = (config->tx_clk_sel | config->rx_clk_sel |
config->rx_fsync_pol | config->tx_fsync_pol |
config->rx_fsync_sel | config->tx_fsync_sel |
config->rx_fifo_config | config->tx_fifo_config |
config->srg_clk_sel | config->loopback_enable |
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);
res = enable_msp(msp, config);
if (res < 0) {
dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n",
__func__, res);
return -EBUSY;
}
if (config->loopback_enable & 0x80)
msp->loopback_enable = 1;
/* Flush FIFOs */
flush_fifo_tx(msp);
flush_fifo_rx(msp);
msp->msp_state = MSP_STATE_CONFIGURED;
return 0;
}
static void disable_msp_rx(struct ux500_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 &
~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT),
msp->registers + MSP_IMSC);
msp->dir_busy &= ~MSP_DIR_RX;
}
static void disable_msp_tx(struct ux500_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 &
~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT),
msp->registers + MSP_IMSC);
msp->dir_busy &= ~MSP_DIR_TX;
}
static int disable_msp(struct ux500_msp *msp, unsigned int dir)
{
u32 reg_val_GCR;
int status = 0;
unsigned int disable_tx, disable_rx;
reg_val_GCR = readl(msp->registers + MSP_GCR);
disable_tx = dir & MSP_DIR_TX;
disable_rx = dir & MSP_DIR_TX;
if (disable_tx && disable_rx) {
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);
disable_msp_tx(msp);
disable_msp_rx(msp);
} else if (disable_tx)
disable_msp_tx(msp);
else if (disable_rx)
disable_msp_rx(msp);
return status;
}
int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
{
u32 reg_val_GCR, enable_bit;
if (msp->msp_state == MSP_STATE_IDLE) {
dev_err(msp->dev, "%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)
disable_msp_tx(msp);
else
disable_msp_rx(msp);
break;
default:
return -EINVAL;
break;
}
return 0;
}
int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
{
int status = 0;
dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir);
status = disable_msp(msp, dir);
if (msp->dir_busy == 0) {
/* disable sample rate and frame generators */
msp->msp_state = MSP_STATE_IDLE;
writel((readl(msp->registers + MSP_GCR) &
(~(FRAME_GEN_ENABLE | SRG_ENABLE))),
msp->registers + MSP_GCR);
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_of_init_msp(struct platform_device *pdev,
struct ux500_msp *msp,
struct msp_i2s_platform_data **platform_data)
{
struct msp_i2s_platform_data *pdata;
*platform_data = devm_kzalloc(&pdev->dev,
sizeof(struct msp_i2s_platform_data),
GFP_KERNEL);
pdata = *platform_data;
if (!pdata)
return -ENOMEM;
msp->playback_dma_data.dma_cfg = devm_kzalloc(&pdev->dev,
sizeof(struct stedma40_chan_cfg),
GFP_KERNEL);
if (!msp->playback_dma_data.dma_cfg)
return -ENOMEM;
msp->capture_dma_data.dma_cfg = devm_kzalloc(&pdev->dev,
sizeof(struct stedma40_chan_cfg),
GFP_KERNEL);
if (!msp->capture_dma_data.dma_cfg)
return -ENOMEM;
return 0;
}
int ux500_msp_i2s_init_msp(struct platform_device *pdev,
struct ux500_msp **msp_p,
struct msp_i2s_platform_data *platform_data)
{
struct resource *res = NULL;
struct device_node *np = pdev->dev.of_node;
struct ux500_msp *msp;
int ret;
*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
msp = *msp_p;
if (!msp)
return -ENOMEM;
if (!platform_data) {
if (np) {
ret = ux500_msp_i2s_of_init_msp(pdev, msp,
&platform_data);
if (ret)
return ret;
} else
return -EINVAL;
} else {
msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx;
msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx;
msp->id = platform_data->id;
}
msp->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n",
__func__);
return -ENOMEM;
}
msp->playback_dma_data.tx_rx_addr = res->start + MSP_DR;
msp->capture_dma_data.tx_rx_addr = res->start + MSP_DR;
msp->registers = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (msp->registers == NULL) {
dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__);
return -ENOMEM;
}
msp->msp_state = MSP_STATE_IDLE;
msp->loopback_enable = 0;
return 0;
}
void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
struct ux500_msp *msp)
{
dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id);
}
MODULE_LICENSE("GPL v2");