From 79cd4a39087c2a75caba0f6730edff3f24c13a08 Mon Sep 17 00:00:00 2001 From: roger nilsson Date: Thu, 9 Feb 2012 13:10:24 +0100 Subject: 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 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50646 Reviewed-by: QABUILD Reviewed-by: Ola LILJA2 Reviewed-by: Jonas ABERG --- sound/soc/ux500/ux500_msp_dai.c | 140 ++++++------------- sound/soc/ux500/ux500_msp_dai.h | 11 -- sound/soc/ux500/ux500_msp_i2s.c | 288 ++++++++-------------------------------- sound/soc/ux500/ux500_msp_i2s.h | 9 +- sound/soc/ux500/ux500_pcm.c | 238 ++++++++++++++++++++++++++------- sound/soc/ux500/ux500_pcm.h | 12 +- 6 files changed, 294 insertions(+), 404 deletions(-) (limited to 'sound') 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 , + * Roger Nilsson , * Sandeep Kaushik * for ST-Ericsson. * @@ -25,8 +26,13 @@ #include #include +#include + #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 #include +#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 #include #include +#include + +#include #include #include #include #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 +#include +#include #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 -- cgit v1.2.3