summaryrefslogtreecommitdiff
path: root/sound/soc/renesas/rz-ssi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/renesas/rz-ssi.c')
-rw-r--r--sound/soc/renesas/rz-ssi.c64
1 files changed, 47 insertions, 17 deletions
diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c
index 81b883e8ac92..f4dc2f68dead 100644
--- a/sound/soc/renesas/rz-ssi.c
+++ b/sound/soc/renesas/rz-ssi.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
+#include <sound/pcm_params.h>
#include <sound/soc.h>
/* REGISTER OFFSET */
@@ -87,7 +88,6 @@ struct rz_ssi_stream {
int dma_buffer_pos; /* The address for the next DMA descriptor */
int completed_dma_buf_pos; /* The address of the last completed DMA descriptor. */
int period_counter; /* for keeping track of periods transferred */
- int sample_width;
int buffer_pos; /* current frame position in the buffer */
int running; /* 0=stopped, 1=running */
@@ -133,6 +133,12 @@ struct rz_ssi_priv {
bool bckp_rise; /* Bit clock polarity (SSICR.BCKP) */
bool dma_rt;
+ struct {
+ bool tx_active;
+ bool rx_active;
+ bool one_stream_triggered;
+ } dup;
+
/* Full duplex communication support */
struct {
unsigned int rate;
@@ -211,10 +217,7 @@ static inline bool rz_ssi_is_stream_running(struct rz_ssi_stream *strm)
static void rz_ssi_stream_init(struct rz_ssi_stream *strm,
struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
rz_ssi_set_substream(strm, substream);
- strm->sample_width = samples_to_bytes(runtime, 1);
strm->dma_buffer_pos = 0;
strm->completed_dma_buf_pos = 0;
strm->period_counter = 0;
@@ -332,13 +335,12 @@ static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
bool is_full_duplex;
u32 ssicr, ssifcr;
- is_full_duplex = rz_ssi_is_stream_running(&ssi->playback) ||
- rz_ssi_is_stream_running(&ssi->capture);
+ is_full_duplex = ssi->dup.tx_active && ssi->dup.rx_active;
ssicr = rz_ssi_reg_readl(ssi, SSICR);
ssifcr = rz_ssi_reg_readl(ssi, SSIFCR);
if (!is_full_duplex) {
ssifcr &= ~0xF;
- } else {
+ } else if (ssi->dup.one_stream_triggered) {
rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0);
rz_ssi_set_idle(ssi);
ssifcr &= ~SSIFCR_FIFO_RST;
@@ -374,12 +376,16 @@ static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
SSISR_RUIRQ), 0);
strm->running = 1;
- if (is_full_duplex)
- ssicr |= SSICR_TEN | SSICR_REN;
- else
+ if (!is_full_duplex) {
ssicr |= is_play ? SSICR_TEN : SSICR_REN;
-
- rz_ssi_reg_writel(ssi, SSICR, ssicr);
+ rz_ssi_reg_writel(ssi, SSICR, ssicr);
+ } else if (ssi->dup.one_stream_triggered) {
+ ssicr |= SSICR_TEN | SSICR_REN;
+ rz_ssi_reg_writel(ssi, SSICR, ssicr);
+ ssi->dup.one_stream_triggered = false;
+ } else {
+ ssi->dup.one_stream_triggered = true;
+ }
return 0;
}
@@ -915,6 +921,30 @@ static int rz_ssi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
+static int rz_ssi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ssi->dup.tx_active = true;
+ else
+ ssi->dup.rx_active = true;
+
+ return 0;
+}
+
+static void rz_ssi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ssi->dup.tx_active = false;
+ else
+ ssi->dup.rx_active = false;
+}
+
static bool rz_ssi_is_valid_hw_params(struct rz_ssi_priv *ssi, unsigned int rate,
unsigned int channels,
unsigned int sample_width,
@@ -945,9 +975,9 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
- struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream);
unsigned int sample_bits = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+ unsigned int sample_width = params_width(params);
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
int ret;
@@ -966,16 +996,14 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
if (rz_ssi_is_stream_running(&ssi->playback) ||
rz_ssi_is_stream_running(&ssi->capture)) {
- if (rz_ssi_is_valid_hw_params(ssi, rate, channels,
- strm->sample_width, sample_bits))
+ if (rz_ssi_is_valid_hw_params(ssi, rate, channels, sample_width, sample_bits))
return 0;
dev_err(ssi->dev, "Full duplex needs same HW params\n");
return -EINVAL;
}
- rz_ssi_cache_hw_params(ssi, rate, channels, strm->sample_width,
- sample_bits);
+ rz_ssi_cache_hw_params(ssi, rate, channels, sample_width, sample_bits);
ret = rz_ssi_swreset(ssi);
if (ret)
@@ -985,6 +1013,8 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
}
static const struct snd_soc_dai_ops rz_ssi_dai_ops = {
+ .startup = rz_ssi_startup,
+ .shutdown = rz_ssi_shutdown,
.trigger = rz_ssi_dai_trigger,
.set_fmt = rz_ssi_dai_set_fmt,
.hw_params = rz_ssi_dai_hw_params,