summaryrefslogtreecommitdiffstats
path: root/sound/soc/atmel/atmel_ssc_dai.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/atmel/atmel_ssc_dai.c')
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c323
1 files changed, 98 insertions, 225 deletions
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 6f89483ac88c..1073f468f21f 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -116,19 +116,16 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
{
.name = "ssc0",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
.dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0,
},
{
.name = "ssc1",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
.dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0,
},
{
.name = "ssc2",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
.dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0,
},
@@ -317,13 +314,10 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
snd_soc_dai_set_dma_data(dai, substream, dma_params);
- spin_lock_irq(&ssc_p->lock);
- if (ssc_p->dir_mask & dir_mask) {
- spin_unlock_irq(&ssc_p->lock);
+ if (ssc_p->dir_mask & dir_mask)
return -EBUSY;
- }
+
ssc_p->dir_mask |= dir_mask;
- spin_unlock_irq(&ssc_p->lock);
return 0;
}
@@ -355,7 +349,6 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
dir_mask = 1 << dir;
- spin_lock_irq(&ssc_p->lock);
ssc_p->dir_mask &= ~dir_mask;
if (!ssc_p->dir_mask) {
if (ssc_p->initialized) {
@@ -369,7 +362,6 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
ssc_p->forced_divider = 0;
}
- spin_unlock_irq(&ssc_p->lock);
/* Shutdown the SSC clock. */
pr_debug("atmel_ssc_dai: Stopping clock\n");
@@ -471,7 +463,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
int dir, channels, bits;
u32 tfmr, rfmr, tcmr, rcmr;
int ret;
- int fslen, fslen_ext;
+ int fslen, fslen_ext, fs_osync, fs_edge;
u32 cmr_div;
u32 tcmr_period;
u32 rcmr_period;
@@ -558,226 +550,45 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
/*
* Compute SSC register settings.
*/
- switch (ssc_p->daifmt
- & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
- /*
- * I2S format, SSC provides BCLK and LRC clocks.
- *
- * The SSC transmit and receive clocks are generated
- * from the MCK divider, and the BCLK signal
- * is output on the SSC TK line.
- */
+ fslen_ext = (bits - 1) / 16;
+ fslen = (bits - 1) % 16;
- if (bits > 16 && !ssc->pdata->has_fslen_ext) {
- dev_err(dai->dev,
- "sample size %d is too large for SSC device\n",
- bits);
- return -EINVAL;
- }
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- fslen_ext = (bits - 1) / 16;
- fslen = (bits - 1) % 16;
-
- rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
- | SSC_BF(RCMR_STTDLY, START_DELAY)
- | SSC_BF(RCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
-
- rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(RFMR_FSLEN, fslen)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, tcmr_period)
- | SSC_BF(TCMR_STTDLY, START_DELAY)
- | SSC_BF(TCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
- | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
-
- tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(TFMR_FSLEN, fslen)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
- break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fs_osync = SSC_FSOS_POSITIVE;
+ fs_edge = SSC_START_RISING_RF;
+
+ rcmr = SSC_BF(RCMR_STTDLY, 0);
+ tcmr = SSC_BF(TCMR_STTDLY, 0);
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
- /* I2S format, CODEC supplies BCLK and LRC clocks. */
- rcmr = SSC_BF(RCMR_PERIOD, 0)
- | SSC_BF(RCMR_STTDLY, START_DELAY)
- | SSC_BF(RCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_PIN : SSC_CKS_CLOCK);
-
- rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
- | SSC_BF(RFMR_FSLEN, 0)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, 0)
- | SSC_BF(TCMR_STTDLY, START_DELAY)
- | SSC_BF(TCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_CLOCK : SSC_CKS_PIN);
-
- tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
- | SSC_BF(TFMR_FSLEN, 0)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
break;
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS:
- /* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */
- if (bits > 16 && !ssc->pdata->has_fslen_ext) {
- dev_err(dai->dev,
- "sample size %d is too large for SSC device\n",
- bits);
- return -EINVAL;
- }
+ case SND_SOC_DAIFMT_I2S:
+ fs_osync = SSC_FSOS_NEGATIVE;
+ fs_edge = SSC_START_FALLING_RF;
- fslen_ext = (bits - 1) / 16;
- fslen = (bits - 1) % 16;
-
- rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
- | SSC_BF(RCMR_STTDLY, START_DELAY)
- | SSC_BF(RCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_PIN : SSC_CKS_CLOCK);
-
- rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(RFMR_FSLEN, fslen)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, tcmr_period)
- | SSC_BF(TCMR_STTDLY, START_DELAY)
- | SSC_BF(TCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_CLOCK : SSC_CKS_PIN);
-
- tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(TFMR_FSLEN, fslen)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
- break;
+ rcmr = SSC_BF(RCMR_STTDLY, 1);
+ tcmr = SSC_BF(TCMR_STTDLY, 1);
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
- /*
- * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
- *
- * The SSC transmit and receive clocks are generated from the
- * MCK divider, and the BCLK signal is output
- * on the SSC TK line.
- */
- rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
- | SSC_BF(RCMR_STTDLY, 1)
- | SSC_BF(RCMR_START, SSC_START_RISING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
-
- rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE)
- | SSC_BF(RFMR_FSLEN, 0)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, tcmr_period)
- | SSC_BF(TCMR_STTDLY, 1)
- | SSC_BF(TCMR_START, SSC_START_RISING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
- | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
-
- tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
- | SSC_BF(TFMR_FSLEN, 0)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
break;
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_DSP_A:
/*
- * DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks.
+ * DSP/PCM Mode A format
*
* Data is transferred on first BCLK after LRC pulse rising
* edge.If stereo, the right channel data is contiguous with
* the left channel data.
*/
- rcmr = SSC_BF(RCMR_PERIOD, 0)
- | SSC_BF(RCMR_STTDLY, START_DELAY)
- | SSC_BF(RCMR_START, SSC_START_RISING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_PIN : SSC_CKS_CLOCK);
-
- rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
- | SSC_BF(RFMR_FSLEN, 0)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, 0)
- | SSC_BF(TCMR_STTDLY, START_DELAY)
- | SSC_BF(TCMR_START, SSC_START_RISING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_CLOCK : SSC_CKS_PIN);
-
- tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
- | SSC_BF(TFMR_FSLEN, 0)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
+ fs_osync = SSC_FSOS_POSITIVE;
+ fs_edge = SSC_START_RISING_RF;
+ fslen = fslen_ext = 0;
+
+ rcmr = SSC_BF(RCMR_STTDLY, 1);
+ tcmr = SSC_BF(TCMR_STTDLY, 1);
+
break;
default:
@@ -785,6 +596,70 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
ssc_p->daifmt);
return -EINVAL;
}
+
+ if (!atmel_ssc_cfs(ssc_p)) {
+ fslen = fslen_ext = 0;
+ rcmr_period = tcmr_period = 0;
+ fs_osync = SSC_FSOS_NONE;
+ }
+
+ rcmr |= SSC_BF(RCMR_START, fs_edge);
+ tcmr |= SSC_BF(TCMR_START, fs_edge);
+
+ if (atmel_ssc_cbs(ssc_p)) {
+ /*
+ * SSC provides BCLK
+ *
+ * The SSC transmit and receive clocks are generated from the
+ * MCK divider, and the BCLK signal is output
+ * on the SSC TK line.
+ */
+ rcmr |= SSC_BF(RCMR_CKS, SSC_CKS_DIV)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE);
+
+ tcmr |= SSC_BF(TCMR_CKS, SSC_CKS_DIV)
+ | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS);
+ } else {
+ rcmr |= SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
+ SSC_CKS_PIN : SSC_CKS_CLOCK)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE);
+
+ tcmr |= SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
+ SSC_CKS_CLOCK : SSC_CKS_PIN)
+ | SSC_BF(TCMR_CKO, SSC_CKO_NONE);
+ }
+
+ rcmr |= SSC_BF(RCMR_PERIOD, rcmr_period)
+ | SSC_BF(RCMR_CKI, SSC_CKI_RISING);
+
+ tcmr |= SSC_BF(TCMR_PERIOD, tcmr_period)
+ | SSC_BF(TCMR_CKI, SSC_CKI_FALLING);
+
+ rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
+ | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(RFMR_FSOS, fs_osync)
+ | SSC_BF(RFMR_FSLEN, fslen)
+ | SSC_BF(RFMR_DATNB, (channels - 1))
+ | SSC_BIT(RFMR_MSBF)
+ | SSC_BF(RFMR_LOOP, 0)
+ | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+ tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
+ | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(TFMR_FSDEN, 0)
+ | SSC_BF(TFMR_FSOS, fs_osync)
+ | SSC_BF(TFMR_FSLEN, fslen)
+ | SSC_BF(TFMR_DATNB, (channels - 1))
+ | SSC_BIT(TFMR_MSBF)
+ | SSC_BF(TFMR_DATDEF, 0)
+ | SSC_BF(TFMR_DATLEN, (bits - 1));
+
+ if (fslen_ext && !ssc->pdata->has_fslen_ext) {
+ dev_err(dai->dev, "sample size %d is too large for SSC device\n",
+ bits);
+ return -EINVAL;
+ }
+
pr_debug("atmel_ssc_hw_params: "
"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
rcmr, rfmr, tcmr, tfmr);
@@ -885,12 +760,12 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
}
#ifdef CONFIG_PM
-static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
+static int atmel_ssc_suspend(struct snd_soc_component *component)
{
struct atmel_ssc_info *ssc_p;
- struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+ struct platform_device *pdev = to_platform_device(component->dev);
- if (!cpu_dai->active)
+ if (!component->active)
return 0;
ssc_p = &ssc_info[pdev->id];
@@ -912,15 +787,13 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
return 0;
}
-
-
-static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
+static int atmel_ssc_resume(struct snd_soc_component *component)
{
struct atmel_ssc_info *ssc_p;
- struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+ struct platform_device *pdev = to_platform_device(component->dev);
u32 cr;
- if (!cpu_dai->active)
+ if (!component->active)
return 0;
ssc_p = &ssc_info[pdev->id];
@@ -964,8 +837,6 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
};
static struct snd_soc_dai_driver atmel_ssc_dai = {
- .suspend = atmel_ssc_suspend,
- .resume = atmel_ssc_resume,
.playback = {
.channels_min = 1,
.channels_max = 2,
@@ -985,6 +856,8 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
static const struct snd_soc_component_driver atmel_ssc_component = {
.name = "atmel-ssc",
+ .suspend = atmel_ssc_suspend,
+ .resume = atmel_ssc_resume,
};
static int asoc_ssc_init(struct device *dev)
OpenPOWER on IntegriCloud