summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorroger nilsson <roger.xr.nilsson@stericsson.com>2012-02-09 13:10:24 +0100
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:05:06 +0200
commit79cd4a39087c2a75caba0f6730edff3f24c13a08 (patch)
treeab6737db658e83777f80c7ed41313edfff66eb9d /sound
parent33fdce6d37048ac39be578523ce1677345706994 (diff)
ASoC: Ux500: Move dma handling to platform driver
The whole audio dma engine has been moved to the ux500 platform driver. ST-Ericsson Linux next: - ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ib399e4f3e8df943c95b4c3df1a1723cdac4936d9 Signed-off-by: roger nilsson <roger.xr.nilsson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50646 Reviewed-by: QABUILD Reviewed-by: Ola LILJA2 <ola.o.lilja@stericsson.com> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c140
-rw-r--r--sound/soc/ux500/ux500_msp_dai.h11
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c288
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h9
-rw-r--r--sound/soc/ux500/ux500_pcm.c238
-rw-r--r--sound/soc/ux500/ux500_pcm.h12
6 files changed, 294 insertions, 404 deletions
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 4a2ff3419f2..5eabb0629d7 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -82,69 +82,6 @@ static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = {
},
};
-bool ux500_msp_dai_i2s_get_underrun_status(int dai_idx)
-{
- struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai_idx];
- int status = ux500_msp_i2s_hw_status(drvdata->msp_i2s_drvdata);
- return (bool)(status & TRANSMIT_UNDERRUN_ERR_INT);
-}
-
-dma_addr_t ux500_msp_dai_i2s_get_pointer(int dai_idx, int stream_id)
-{
- struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai_idx];
- return ux500_msp_i2s_get_pointer(drvdata->msp_i2s_drvdata,
- (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ?
- I2S_DIRECTION_TX :
- I2S_DIRECTION_RX);
-}
-
-int ux500_msp_dai_i2s_configure_sg(dma_addr_t dma_addr,
- int period_cnt,
- size_t period_len,
- int dai_idx,
- int stream_id)
-{
- struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai_idx];
- struct i2s_message message;
- int ret = 0;
- bool playback_req_valid =
- (drvdata->playback_active &&
- stream_id == SNDRV_PCM_STREAM_PLAYBACK);
- bool capture_req_valid =
- (drvdata->capture_active &&
- stream_id == SNDRV_PCM_STREAM_CAPTURE);
-
- pr_debug("%s: Enter (MSP Index: %u, period-cnt: %u, period-len: %u).\n",
- __func__,
- dai_idx,
- period_cnt,
- period_len);
-
- if (!playback_req_valid && !capture_req_valid) {
- pr_err("%s: The I2S controller is not available."
- "MSP index:%d\n",
- __func__,
- dai_idx);
- return ret;
- }
-
- message.i2s_direction = (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ?
- I2S_DIRECTION_TX :
- I2S_DIRECTION_RX;
- message.buf_addr = dma_addr;
- message.buf_len = period_cnt * period_len;
- message.period_len = period_len;
-
- ret = ux500_msp_i2s_transfer(drvdata->msp_i2s_drvdata, &message);
- if (ret < 0) {
- pr_err("%s: Error: i2s_transfer failed. MSP index: %d\n",
- __func__,
- dai_idx);
- }
-
- return ret;
-}
-
static const char *stream_str(struct snd_pcm_substream *substream)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -448,13 +385,6 @@ static void ux500_msp_dai_compile_msp_config(struct snd_pcm_substream *substream
msp_config->rx_fifo_config = RX_FIFO_ENABLE;
msp_config->spi_clk_mode = SPI_CLK_MODE_NORMAL;
msp_config->spi_burst_mode = 0;
- msp_config->handler = ux500_pcm_dma_eot_handler;
- msp_config->tx_callback_data =
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
- substream : NULL;
- msp_config->rx_callback_data =
- substream->stream == SNDRV_PCM_STREAM_CAPTURE ?
- substream : NULL;
msp_config->def_elem_len = 1;
msp_config->direction =
substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
@@ -523,18 +453,25 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct msp_config msp_config;
bool mode_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u8 configflag = mode_playback ? PLAYBACK_CONFIGURED : CAPTURE_CONFIGURED;
pr_debug("%s: MSP %d (%s): Enter.\n", __func__, dai->id, stream_str(substream));
- /* If already configured -> not errors reported */
- if (mode_playback) {
- if ((drvdata->configured & PLAYBACK_CONFIGURED) &&
- (drvdata->playback_active))
- goto cleanup;
- } else {
- if ((drvdata->configured & CAPTURE_CONFIGURED) &&
- (drvdata->capture_active))
+ if (configflag & drvdata->configured) {
+
+ ret = ux500_msp_i2s_close(
+ drvdata->msp_i2s_drvdata,
+ mode_playback ? DISABLE_TRANSMIT : DISABLE_RECEIVE);
+
+ if (ret) {
+ pr_err("%s: Error: MSP %d (%s): Unable to close i2s.\n",
+ __func__,
+ dai->id,
+ stream_str(substream));
goto cleanup;
+ }
+
+ drvdata->configured &= ~configflag;
}
pr_debug("%s: Setup dai (Rate: %u).\n", __func__, runtime->rate);
@@ -549,8 +486,7 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
goto cleanup;
}
- drvdata->configured |= mode_playback ?
- PLAYBACK_CONFIGURED : CAPTURE_CONFIGURED;
+ drvdata->configured |= configflag;
cleanup:
return ret;
@@ -766,32 +702,35 @@ static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream,
(int)drvdata->msp_i2s_drvdata->id,
cmd);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- ret = 0;
- break;
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = 0;
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- ret = 0;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = 0;
- break;
- default:
- ret = -EINVAL;
- break;
- }
+ ret = ux500_msp_i2s_trigger(drvdata->msp_i2s_drvdata,
+ cmd,
+ substream->stream);
return ret;
}
+static int
+ux500_msp_dai_probe(struct snd_soc_dai *dai)
+{
+ struct ux500_msp_i2s_drvdata *drvdata = snd_soc_dai_get_drvdata(dai);
+ struct ux500_platform_drvdata *private = &platform_drvdata[dai->id];
+
+ drvdata->playback_dma_data.dma_cfg = drvdata->msp->dma_cfg_tx;
+ drvdata->capture_dma_data.dma_cfg = drvdata->msp->dma_cfg_rx;
+
+ dai->playback_dma_data = &drvdata->playback_dma_data;
+ dai->capture_dma_data = &drvdata->capture_dma_data;
+
+ drvdata->playback_dma_data.data_size = private->slot_width;
+ drvdata->capture_dma_data.data_size = private->slot_width;
+
+ return 0;
+}
+
static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
{
.name = "ux500-msp-i2s.0",
+ .probe = ux500_msp_dai_probe,
.id = 0,
.suspend = NULL,
.resume = NULL,
@@ -822,6 +761,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
},
{
.name = "ux500-msp-i2s.1",
+ .probe = ux500_msp_dai_probe,
.id = 1,
.suspend = NULL,
.resume = NULL,
@@ -853,6 +793,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
{
.name = "ux500-msp-i2s.2",
.id = 2,
+ .probe = ux500_msp_dai_probe,
.suspend = NULL,
.resume = NULL,
.playback = {
@@ -882,6 +823,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
},
{
.name = "ux500-msp-i2s.3",
+ .probe = ux500_msp_dai_probe,
.id = 3,
.suspend = NULL,
.resume = NULL,
diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h
index c44894526f2..f5bda875214 100644
--- a/sound/soc/ux500/ux500_msp_dai.h
+++ b/sound/soc/ux500/ux500_msp_dai.h
@@ -67,17 +67,6 @@ struct ux500_platform_drvdata {
};
extern struct snd_soc_dai ux500_msp_dai[UX500_NBR_OF_DAI];
-
-bool ux500_msp_dai_i2s_get_underrun_status(int dai_idx);
-dma_addr_t ux500_msp_dai_i2s_get_pointer(int dai_idx, int stream_id);
-int ux500_msp_dai_i2s_configure_sg(dma_addr_t dma_addr,
- int perod_cnt,
- size_t period_len,
- int dai_idx,
- int stream_id);
-int ux500_msp_dai_i2s_send_data(void *data, size_t bytes, int dai_idx);
-int ux500_msp_dai_i2s_receive_data(void *data, size_t bytes, int dai_idx);
-
int ux500_msp_dai_set_data_delay(struct snd_soc_dai *dai, int delay);
#endif
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index f8138bd1aa3..98024af03eb 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -2,6 +2,7 @@
* 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.
*
@@ -25,8 +26,13 @@
#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,
@@ -309,124 +315,6 @@ static int ux500_msp_i2s_configure_multichannel(struct msp *msp, struct msp_conf
return 0;
}
-void ux500_msp_i2s_configure_dma(struct msp *msp, struct msp_config *config)
-{
- struct stedma40_chan_cfg *rx_dma_info = msp->dma_cfg_rx;
- struct stedma40_chan_cfg *tx_dma_info = msp->dma_cfg_tx;
- dma_cap_mask_t mask;
- u16 word_width;
- bool rx_active, tx_active;
-
- if ((msp->tx_pipeid != NULL) &&
- (config->direction == MSP_TRANSMIT_MODE)) {
- dma_release_channel(msp->tx_pipeid);
- msp->tx_pipeid = NULL;
- }
-
- if (msp->rx_pipeid != NULL) {
- dma_release_channel(msp->rx_pipeid);
- msp->rx_pipeid = NULL;
- }
-
- switch (config->data_size) {
- case MSP_DATA_BITS_32:
- word_width = STEDMA40_WORD_WIDTH;
- break;
- case MSP_DATA_BITS_16:
- word_width = STEDMA40_HALFWORD_WIDTH;
- break;
- case MSP_DATA_BITS_8:
- word_width = STEDMA40_BYTE_WIDTH;
- break;
- default:
- word_width = STEDMA40_WORD_WIDTH;
- pr_warn("%s: Unknown data-size (%d)! Assuming 32 bits.\n",
- __func__, config->data_size);
- }
-
- rx_active = (config->direction == MSP_RECEIVE_MODE ||
- config->direction == MSP_BOTH_T_R_MODE);
- tx_active = (config->direction == MSP_TRANSMIT_MODE ||
- config->direction == MSP_BOTH_T_R_MODE);
-
- if (rx_active) {
- rx_dma_info->src_info.data_width = word_width;
- rx_dma_info->dst_info.data_width = word_width;
- }
- if (tx_active) {
- tx_dma_info->src_info.data_width = word_width;
- tx_dma_info->dst_info.data_width = word_width;
- }
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- if (rx_active)
- msp->rx_pipeid = dma_request_channel(mask, stedma40_filter, rx_dma_info);
-
- if (tx_active)
- msp->tx_pipeid = dma_request_channel(mask, stedma40_filter, tx_dma_info);
-}
-
-static int ux500_msp_i2s_dma_xfer(struct msp *msp, struct i2s_message *msg)
-{
- dma_cookie_t status_submit;
- int direction, enable_bit;
- u32 reg_val_GCR;
- struct dma_chan *pipeid;
- struct dma_async_tx_descriptor *cdesc;
-
- if (msg->i2s_direction == I2S_DIRECTION_TX) {
- direction = DMA_TO_DEVICE;
- pipeid = msp->tx_pipeid;
- enable_bit = TX_ENABLE;
- pr_debug("%s: Direction: TX\n", __func__);
- } else {
- direction = DMA_FROM_DEVICE;
- pipeid = msp->rx_pipeid;
- enable_bit = RX_ENABLE;
- pr_debug("%s: Direction: RX\n", __func__);
- }
-
- pr_debug("%s: msg->buf_addr = %p\n", __func__, (void *)msg->buf_addr);
- pr_debug("%s: buf_len = %d\n", __func__, msg->buf_len);
- pr_debug("%s: perios_len = %d\n", __func__, msg->period_len);
-
- /* setup the cyclic description */
- cdesc = pipeid->device->device_prep_dma_cyclic(pipeid,
- msg->buf_addr,
- msg->buf_len,
- msg->period_len,
- direction);
- if (IS_ERR(cdesc)) {
- pr_err("%s: ERROR: device_prep_dma_cyclic failed (%ld)!\n",
- __func__,
- PTR_ERR(cdesc));
- return -EINVAL;
- }
-
- /* Submit to the dma */
- if (msg->i2s_direction == I2S_DIRECTION_TX) {
- cdesc->callback = msp->xfer_data.tx_handler;
- cdesc->callback_param = msp->xfer_data.tx_callback_data;
- } else {
- cdesc->callback = msp->xfer_data.rx_handler;
- cdesc->callback_param = msp->xfer_data.rx_callback_data;
- }
- status_submit = dmaengine_submit(cdesc);
- if (dma_submit_error(status_submit)) {
- pr_err("%s: ERROR: dmaengine_submit failed!\n", __func__);
- return -EINVAL;
- }
-
- /* Start the dma */
- dma_async_issue_pending(pipeid);
- reg_val_GCR = readl(msp->registers + MSP_GCR);
- writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR);
-
- return 0;
-}
-
static int ux500_msp_i2s_enable(struct msp *msp, struct msp_config *config)
{
int status = 0;
@@ -483,27 +371,14 @@ static int ux500_msp_i2s_enable(struct msp *msp, struct msp_config *config)
writel(reg_val_DMACR | TX_DMA_ENABLE,
msp->registers + MSP_DMACR);
- msp->xfer_data.tx_handler = config->handler;
- msp->xfer_data.tx_callback_data = config->tx_callback_data;
-
break;
case MSP_RECEIVE_MODE:
writel(reg_val_DMACR | RX_DMA_ENABLE,
msp->registers + MSP_DMACR);
-
- msp->xfer_data.rx_handler = config->handler;
- msp->xfer_data.rx_callback_data = config->rx_callback_data;
-
break;
case MSP_BOTH_T_R_MODE:
writel(reg_val_DMACR | RX_DMA_ENABLE | TX_DMA_ENABLE,
msp->registers + MSP_DMACR);
-
- msp->xfer_data.tx_handler = config->handler;
- msp->xfer_data.rx_handler = config->handler;
- msp->xfer_data.tx_callback_data = config->tx_callback_data;
- msp->xfer_data.rx_callback_data = config->rx_callback_data;
-
break;
default:
pr_err("%s: ERROR: Illegal MSP direction (config->direction = %d)!",
@@ -513,10 +388,6 @@ static int ux500_msp_i2s_enable(struct msp *msp, struct msp_config *config)
msp->plat_exit();
return -EINVAL;
}
- ux500_msp_i2s_configure_dma(msp, config);
-
- msp->transfer = ux500_msp_i2s_dma_xfer;
-
writel(config->iodelay, msp->registers + MSP_IODLY);
/* Enable frame generation logic */
@@ -645,40 +516,6 @@ int ux500_msp_i2s_open(struct ux500_msp_i2s_drvdata *drvdata, struct msp_config
return 0;
}
-static void func_notify_timer(unsigned long data)
-{
- struct msp *msp = (struct msp *)data;
- if (msp->polling_flag) {
- msp->msp_io_error = 1;
- pr_err("%s: ERROR: Polling timeout!\n", __func__);
- del_timer(&msp->notify_timer);
- }
-}
-
-int ux500_msp_i2s_transfer(struct ux500_msp_i2s_drvdata *drvdata, struct i2s_message *message)
-{
- struct msp *msp = drvdata->msp;
- int status = 0;
-
- if (!message || (msp->msp_state == MSP_STATE_IDLE)) {
- pr_err("%s: ERROR: i2s_message == NULL!\n", __func__);
- return -EINVAL;
- }
- if (msp->msp_state == MSP_STATE_IDLE) {
- pr_err("%s: ERROR: MSP in idle-state!\n", __func__);
- return -EPERM;
- }
-
- msp->msp_state = MSP_STATE_RUN;
- if (msp->transfer)
- status = msp->transfer(msp, message);
-
- if (msp->msp_state == MSP_STATE_RUN)
- msp->msp_state = MSP_STATE_CONFIGURED;
-
- return status;
-}
-
static void ux500_msp_i2s_disable_rx(struct msp *msp)
{
u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
@@ -691,10 +528,6 @@ static void ux500_msp_i2s_disable_rx(struct msp *msp)
writel(reg_val_IMSC &
~(RECEIVE_SERVICE_INT | RECEIVE_OVERRUN_ERROR_INT),
msp->registers + MSP_IMSC);
- msp->xfer_data.message.rxbytes = 0;
- msp->xfer_data.message.rx_offset = 0;
- msp->xfer_data.message.rxdata = NULL;
- msp->read = NULL;
}
static void ux500_msp_i2s_disable_tx(struct msp *msp)
@@ -709,10 +542,6 @@ static void ux500_msp_i2s_disable_tx(struct msp *msp)
writel(reg_val_IMSC &
~(TRANSMIT_SERVICE_INT | TRANSMIT_UNDERRUN_ERR_INT),
msp->registers + MSP_IMSC);
- msp->xfer_data.message.txbytes = 0;
- msp->xfer_data.message.tx_offset = 0;
- msp->xfer_data.message.txdata = NULL;
- msp->write = NULL;
}
static int ux500_msp_i2s_disable(struct msp *msp, int direction, enum i2s_flag flag)
@@ -721,23 +550,6 @@ static int ux500_msp_i2s_disable(struct msp *msp, int direction, enum i2s_flag f
int status = 0;
reg_val_GCR = readl(msp->registers + MSP_GCR);
- if (!(reg_val_GCR & (TX_ENABLE | RX_ENABLE)))
- return 0;
-
- if (flag == DISABLE_ALL || flag == DISABLE_TRANSMIT) {
- if (msp->tx_pipeid != NULL) {
- dmaengine_terminate_all(msp->tx_pipeid);
- dma_release_channel(msp->tx_pipeid);
- msp->tx_pipeid = NULL;
- }
- }
- if ((flag == DISABLE_ALL || flag == DISABLE_RECEIVE)) {
- if (msp->rx_pipeid != NULL) {
- dmaengine_terminate_all(msp->rx_pipeid);
- dma_release_channel(msp->rx_pipeid);
- msp->rx_pipeid = NULL;
- }
- }
if (flag == DISABLE_TRANSMIT)
ux500_msp_i2s_disable_tx(msp);
@@ -774,13 +586,11 @@ static int ux500_msp_i2s_disable(struct msp *msp, int direction, enum i2s_flag f
writel((readl(msp->registers + MSP_GCR) &
(~(FRAME_GEN_ENABLE | SRG_ENABLE))),
msp->registers + MSP_GCR);
- memset(&msp->xfer_data, 0, sizeof(struct trans_data));
if (msp->plat_exit)
status = msp->plat_exit();
if (status)
pr_warn("%s: WARN: ux500_msp_i2s_exit failed (%d)!\n",
__func__, status);
- msp->transfer = NULL;
writel(0, msp->registers + MSP_GCR);
writel(0, msp->registers + MSP_TCF);
writel(0, msp->registers + MSP_RCF);
@@ -802,6 +612,50 @@ static int ux500_msp_i2s_disable(struct msp *msp, int direction, enum i2s_flag f
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;
@@ -852,32 +706,6 @@ end:
}
-int ux500_msp_i2s_hw_status(struct ux500_msp_i2s_drvdata *drvdata)
-{
- struct msp *msp = drvdata->msp;
- int status;
-
- pr_debug("%s: Enter.\n", __func__);
-
- status = readl(msp->registers + MSP_RIS) & 0xee;
- if (status)
- writel(status, msp->registers + MSP_ICR);
-
- return status;
-}
-
-dma_addr_t ux500_msp_i2s_get_pointer(struct ux500_msp_i2s_drvdata *drvdata,
- enum i2s_direction_t i2s_direction)
-{
- struct msp *msp = drvdata->msp;
-
- pr_debug("%s: Enter.\n", __func__);
-
- return (i2s_direction == I2S_DIRECTION_TX) ?
- stedma40_get_src_addr(msp->tx_pipeid) :
- stedma40_get_dst_addr(msp->rx_pipeid);
-}
-
struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev,
struct msp_i2s_platform_data *platform_data)
{
@@ -934,16 +762,6 @@ struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev,
goto free_irq;
}
- init_timer(&msp->notify_timer);
- msp->notify_timer.expires = jiffies + msecs_to_jiffies(1000);
- msp->notify_timer.function = func_notify_timer;
- msp->notify_timer.data = (unsigned long)msp;
-
- msp->rx_pipeid = NULL;
- msp->tx_pipeid = NULL;
- msp->read = NULL;
- msp->write = NULL;
- msp->transfer = NULL;
msp->msp_state = MSP_STATE_IDLE;
msp->loopback_enable = 0;
@@ -952,7 +770,7 @@ struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev,
if (!i2s_cont) {
pr_err("%s: ERROR: Failed to allocate struct i2s_cont (kzalloc)!\n",
__func__);
- goto del_timer;
+ goto del_clk;
}
i2s_cont->dev.parent = &pdev->dev;
i2s_cont->data = (void *)msp;
@@ -966,8 +784,7 @@ struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev,
return msp_i2s_drvdata;
-del_timer:
- del_timer_sync(&msp->notify_timer);
+del_clk:
clk_put(msp->clk);
free_irq:
iounmap(msp->registers);
@@ -984,7 +801,6 @@ int ux500_msp_i2s_exit(struct ux500_msp_i2s_drvdata *drvdata)
pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id);
device_unregister(&msp->i2s_cont->dev);
- del_timer_sync(&msp->notify_timer);
clk_put(msp->clk);
iounmap(msp->registers);
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
index db88d0ca5de..a3a2338a8b1 100644
--- a/sound/soc/ux500/ux500_msp_i2s.h
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -17,11 +17,14 @@
#include <linux/platform_device.h>
#include <mach/msp.h>
+#include "ux500_pcm.h"
struct ux500_msp_i2s_drvdata {
int id;
struct msp *msp;
struct regulator *reg_vape;
+ struct ux500_pcm_dma_params playback_dma_data;
+ struct ux500_pcm_dma_params capture_dma_data;
};
struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev,
@@ -29,11 +32,7 @@ struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev,
int ux500_msp_i2s_exit(struct ux500_msp_i2s_drvdata *drvdata);
int ux500_msp_i2s_open(struct ux500_msp_i2s_drvdata *drvdata, struct msp_config *msp_config);
int ux500_msp_i2s_close(struct ux500_msp_i2s_drvdata *drvdata, enum i2s_flag flag);
-int ux500_msp_i2s_transfer(struct ux500_msp_i2s_drvdata *drvdata, struct i2s_message *message);
-int ux500_msp_i2s_hw_status(struct ux500_msp_i2s_drvdata *drvdata);
-dma_addr_t ux500_msp_i2s_get_pointer(struct ux500_msp_i2s_drvdata *drvdata,
- enum i2s_direction_t i2s_direction);
-
+int ux500_msp_i2s_trigger(struct ux500_msp_i2s_drvdata *drvdata, int cmd, int direction);
int ux500_msp_i2s_suspend(struct ux500_msp_i2s_drvdata *drvdata);
int ux500_msp_i2s_resume(struct ux500_msp_i2s_drvdata *drvdata);
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
index 07e18135451..7616bbb09af 100644
--- a/sound/soc/ux500/ux500_pcm.c
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -19,13 +19,15 @@
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <plat/ste_dma40.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "ux500_pcm.h"
-#include "ux500_msp_dai.h"
static struct snd_pcm_hardware ux500_pcm_hw_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
@@ -77,28 +79,8 @@ static const char *stream_str(struct snd_pcm_substream *substream)
return "Capture";
}
-static void ux500_pcm_dma_hw_free(struct device *dev,
- struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = runtime->dma_buffer_p;
-
- if (runtime->dma_area == NULL)
- return;
-
- if (buf != &substream->dma_buffer) {
- dma_free_coherent(
- buf->dev.dev,
- buf->bytes,
- buf->area,
- buf->addr);
- kfree(runtime->dma_buffer_p);
- }
-
- snd_pcm_set_runtime_buffer(substream, NULL);
-}
-
-void ux500_pcm_dma_eot_handler(void *data)
+static void
+ux500_pcm_dma_eot_handler(void *data)
{
struct snd_pcm_substream *substream = data;
struct snd_pcm_runtime *runtime;
@@ -106,18 +88,14 @@ void ux500_pcm_dma_eot_handler(void *data)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *dai = rtd->cpu_dai;
- pr_debug("%s: MSP %d (%s): Enter.\n", __func__, dai->id, stream_str(substream));
+ pr_debug("%s: MSP %d (%s): Enter.\n", __func__,
+ dai->id,
+ stream_str(substream));
if (substream) {
runtime = substream->runtime;
private = substream->runtime->private_data;
- if (ux500_msp_dai_i2s_get_underrun_status(private->msp_id)) {
- private->no_of_underruns++;
- pr_debug("%s: Nr of underruns (%d)\n", __func__,
- private->no_of_underruns);
- }
-
/* calc the offset in the circular buffer */
private->offset += frames_to_bytes(runtime,
runtime->period_size);
@@ -127,7 +105,98 @@ void ux500_pcm_dma_eot_handler(void *data)
snd_pcm_period_elapsed(substream);
}
}
-EXPORT_SYMBOL(ux500_pcm_dma_eot_handler);
+
+static int
+ux500_pcm_dma_start(
+ struct snd_pcm_substream *substream,
+ dma_addr_t dma_addr,
+ int period_cnt,
+ size_t period_len,
+ int dai_idx,
+ int stream_id)
+{
+ dma_cookie_t status_submit;
+ int direction;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct ux500_pcm_private *private = runtime->private_data;
+ struct dma_async_tx_descriptor *cdesc;
+ struct ux500_pcm_dma_params *dma_params;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ dma_params = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ direction = DMA_TO_DEVICE;
+ else
+ direction = DMA_FROM_DEVICE;
+
+ cdesc = private->pipeid->device->device_prep_dma_cyclic(
+ private->pipeid,
+ dma_addr,
+ period_cnt * period_len,
+ period_len,
+ direction);
+
+ if (IS_ERR(cdesc)) {
+ pr_err("%s: ERROR: device_prep_dma_cyclic failed (%ld)!\n",
+ __func__,
+ PTR_ERR(cdesc));
+ return -EINVAL;
+ }
+
+ cdesc->callback = ux500_pcm_dma_eot_handler;
+ cdesc->callback_param = substream;
+
+ status_submit = dmaengine_submit(cdesc);
+
+ if (dma_submit_error(status_submit)) {
+ pr_err("%s: ERROR: dmaengine_submit failed!\n", __func__);
+ return -EINVAL;
+ }
+
+ dma_async_issue_pending(private->pipeid);
+
+ return 0;
+}
+
+static void
+ux500_pcm_dma_stop(struct work_struct *work)
+{
+ struct ux500_pcm_private *private = container_of(work,
+ struct ux500_pcm_private, ws_stop);
+
+ if (private->pipeid != NULL) {
+ dmaengine_terminate_all(private->pipeid);
+ dma_release_channel(private->pipeid);
+ private->pipeid = NULL;
+ }
+}
+
+static void
+ux500_pcm_dma_hw_free(
+ struct device *dev,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+
+ if (runtime->dma_area == NULL)
+ return;
+
+ if (buf != &substream->dma_buffer) {
+ dma_free_coherent(
+ buf->dev.dev,
+ buf->bytes,
+ buf->area,
+ buf->addr);
+ kfree(runtime->dma_buffer_p);
+ }
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+}
static int ux500_pcm_open(struct snd_pcm_substream *substream)
{
@@ -138,7 +207,9 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream)
struct snd_soc_dai *dai = rtd->cpu_dai;
int ret;
- pr_debug("%s: MSP %d (%s): Enter.\n", __func__, dai->id, stream_str(substream));
+ pr_debug("%s: MSP %d (%s): Enter.\n", __func__,
+ dai->id,
+ stream_str(substream));
pr_debug("%s: Set runtime hwparams.\n", __func__);
if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
@@ -147,7 +218,10 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream)
snd_soc_set_runtime_hwparams(substream, &ux500_pcm_hw_capture);
/* ensure that buffer size is a multiple of period size */
- ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ ret = snd_pcm_hw_constraint_integer(
+ runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+
if (ret < 0) {
pr_err("%s: Error: snd_pcm_hw_constraints failed (%d)\n",
__func__,
@@ -160,9 +234,15 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream)
if (private == NULL)
return -ENOMEM;
private->msp_id = dai->id;
+ private->wq = alloc_ordered_workqueue("ux500/pcm", 0);
+ INIT_WORK(&private->ws_stop, ux500_pcm_dma_stop);
+
runtime->private_data = private;
- pr_debug("%s: Set hw-struct for %s.\n", __func__, stream_str(substream));
+ pr_debug("%s: Set hw-struct for %s.\n",
+ __func__,
+ stream_str(substream));
+
runtime->hw = (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ?
ux500_pcm_hw_playback : ux500_pcm_hw_capture;
@@ -175,6 +255,12 @@ static int ux500_pcm_close(struct snd_pcm_substream *substream)
pr_debug("%s: Enter\n", __func__);
+ if (private->pipeid != NULL) {
+ dma_release_channel(private->pipeid);
+ private->pipeid = NULL;
+ }
+
+ destroy_workqueue(private->wq);
kfree(private);
return 0;
@@ -231,7 +317,8 @@ static int ux500_pcm_hw_params(struct snd_pcm_substream *substream,
return -ENOMEM;
}
-static int ux500_pcm_hw_free(struct snd_pcm_substream *substream)
+static int
+ux500_pcm_hw_free(struct snd_pcm_substream *substream)
{
pr_debug("%s: Enter\n", __func__);
@@ -240,15 +327,67 @@ static int ux500_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int ux500_pcm_prepare(struct snd_pcm_substream *substream)
+static int
+ux500_pcm_prepare(struct snd_pcm_substream *substream)
{
+ struct stedma40_chan_cfg *dma_cfg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct ux500_pcm_private *private = runtime->private_data;
+ struct ux500_pcm_dma_params *dma_params;
+ dma_cap_mask_t mask;
+ u16 per_data_width, mem_data_width;
+
pr_debug("%s: Enter\n", __func__);
+
+ if (private->pipeid != NULL) {
+ dma_release_channel(private->pipeid);
+ private->pipeid = NULL;
+ }
+
+ dma_params = snd_soc_dai_get_dma_data(dai, substream);
+
+ mem_data_width = STEDMA40_HALFWORD_WIDTH;
+
+ switch (dma_params->data_size) {
+ case 32:
+ per_data_width = STEDMA40_WORD_WIDTH;
+ break;
+ case 16:
+ per_data_width = STEDMA40_HALFWORD_WIDTH;
+ break;
+ case 8:
+ per_data_width = STEDMA40_BYTE_WIDTH;
+ break;
+ default:
+ per_data_width = STEDMA40_WORD_WIDTH;
+ pr_warn("%s: Unknown data-size (%d)! Assuming 32 bits.\n",
+ __func__, dma_params->data_size);
+ }
+
+ dma_cfg = dma_params->dma_cfg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_cfg->src_info.data_width = mem_data_width;
+ dma_cfg->dst_info.data_width = per_data_width;
+ } else {
+ dma_cfg->src_info.data_width = per_data_width;
+ dma_cfg->dst_info.data_width = mem_data_width;
+ }
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ private->pipeid = dma_request_channel(mask, stedma40_filter, dma_cfg);
+
return 0;
}
-static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int
+ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- int ret;
+ int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct ux500_pcm_private *private = runtime->private_data;
int stream_id = substream->pstr->stream;
@@ -264,9 +403,10 @@ static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
- private->no_of_underruns = 0;
private->offset = 0;
- ret = ux500_msp_dai_i2s_configure_sg(runtime->dma_addr,
+ ret = ux500_pcm_dma_start(
+ substream,
+ runtime->dma_addr,
runtime->periods,
frames_to_bytes(runtime, runtime->period_size),
private->msp_id,
@@ -277,16 +417,11 @@ static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
break;
case SNDRV_PCM_TRIGGER_RESUME:
- break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- break;
-
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
- pr_debug("%s: no_of_underruns = %u\n",
- __func__,
- private->no_of_underruns);
+ queue_work(private->wq, &private->ws_stop);
break;
default:
@@ -295,10 +430,11 @@ static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return -EINVAL;
}
- return 0;
+ return ret;
}
-static snd_pcm_uframes_t ux500_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t
+ux500_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct ux500_pcm_private *private = runtime->private_data;
@@ -309,8 +445,10 @@ static snd_pcm_uframes_t ux500_pcm_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(substream->runtime, private->offset);
}
-static int ux500_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static int
+ux500_pcm_mmap(
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
pr_debug("%s: Enter.\n", __func__);
diff --git a/sound/soc/ux500/ux500_pcm.h b/sound/soc/ux500/ux500_pcm.h
index 50f46615275..3d1ea4f8395 100644
--- a/sound/soc/ux500/ux500_pcm.h
+++ b/sound/soc/ux500/ux500_pcm.h
@@ -14,7 +14,8 @@
#ifndef UX500_PCM_H
#define UX500_PCM_H
-#include <mach/msp.h>
+#include <asm/page.h>
+#include <linux/workqueue.h>
#define UX500_PLATFORM_MIN_RATE_PLAYBACK 8000
#define UX500_PLATFORM_MAX_RATE_PLAYBACK 48000
@@ -33,12 +34,17 @@
extern struct snd_soc_platform ux500_soc_platform;
struct ux500_pcm_private {
+ struct dma_chan *pipeid;
+ struct workqueue_struct *wq;
+ struct work_struct ws_stop;
int msp_id;
int stream_id;
- unsigned int no_of_underruns;
unsigned int offset;
};
-void ux500_pcm_dma_eot_handler(void *data);
+struct ux500_pcm_dma_params {
+ unsigned int data_size;
+ struct stedma40_chan_cfg *dma_cfg;
+};
#endif