aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/pxa/pxa-ssp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/pxa/pxa-ssp.c')
-rw-r--r--sound/soc/pxa/pxa-ssp.c49
1 files changed, 38 insertions, 11 deletions
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 7acd3febf8b..286be31545d 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -280,12 +280,33 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
* ssp_set_clkdiv - set SSP clock divider
* @div: serial clock rate divider
*/
-static void ssp_set_scr(struct ssp_dev *dev, u32 div)
+static void ssp_set_scr(struct ssp_device *ssp, u32 div)
{
- struct ssp_device *ssp = dev->ssp;
- u32 sscr0 = ssp_read_reg(dev->ssp, SSCR0) & ~SSCR0_SCR;
+ u32 sscr0 = ssp_read_reg(ssp, SSCR0);
+
+ if (cpu_is_pxa25x() && ssp->type == PXA25x_SSP) {
+ sscr0 &= ~0x0000ff00;
+ sscr0 |= ((div - 2)/2) << 8; /* 2..512 */
+ } else {
+ sscr0 &= ~0x000fff00;
+ sscr0 |= (div - 1) << 8; /* 1..4096 */
+ }
+ ssp_write_reg(ssp, SSCR0, sscr0);
+}
+
+/**
+ * ssp_get_clkdiv - get SSP clock divider
+ */
+static u32 ssp_get_scr(struct ssp_device *ssp)
+{
+ u32 sscr0 = ssp_read_reg(ssp, SSCR0);
+ u32 div;
- ssp_write_reg(ssp, SSCR0, (sscr0 | SSCR0_SerClkDiv(div)));
+ if (cpu_is_pxa25x() && ssp->type == PXA25x_SSP)
+ div = ((sscr0 >> 8) & 0xff) * 2 + 2;
+ else
+ div = ((sscr0 >> 8) & 0xfff) + 1;
+ return div;
}
/*
@@ -326,7 +347,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
break;
case PXA_SSP_CLK_AUDIO:
priv->sysclk = 0;
- ssp_set_scr(&priv->dev, 1);
+ ssp_set_scr(ssp, 1);
sscr0 |= SSCR0_ACS;
break;
default:
@@ -387,7 +408,7 @@ static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
ssp_write_reg(ssp, SSACD, val);
break;
case PXA_SSP_DIV_SCR:
- ssp_set_scr(&priv->dev, div);
+ ssp_set_scr(ssp, div);
break;
default:
return -ENODEV;
@@ -627,12 +648,18 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
u32 sscr0;
u32 sspsp;
int width = snd_pcm_format_physical_width(params_format(params));
+ int ttsa = ssp_read_reg(ssp, SSTSA) & 0xf;
/* select correct DMA params */
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
dma = 1; /* capture DMA offset is 1,3 */
- if (chn == 2)
- dma += 2; /* stereo DMA offset is 2, mono is 0 */
+ /* Network mode with one active slot (ttsa == 1) can be used
+ * to force 16-bit frame width on the wire (for S16_LE), even
+ * with two channels. Use 16-bit DMA transfers for this case.
+ */
+ if (((chn == 2) && (ttsa != 1)) || (width == 32))
+ dma += 2; /* 32-bit DMA offset is 2, 16-bit is 0 */
+
cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
dev_dbg(&ssp->pdev->dev, "pxa_ssp_hw_params: dma %d\n", dma);
@@ -668,8 +695,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
case SND_SOC_DAIFMT_I2S:
sspsp = ssp_read_reg(ssp, SSPSP);
- if (((sscr0 & SSCR0_SCR) == SSCR0_SerClkDiv(4)) &&
- (width == 16)) {
+ if ((ssp_get_scr(ssp) == 4) && (width == 16)) {
/* This is a special case where the bitclk is 64fs
* and we're not dealing with 2*32 bits of audio
* samples.
@@ -712,7 +738,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
/* When we use a network mode, we always require TDM slots
* - complain loudly and fail if they've not been set up yet.
*/
- if ((sscr0 & SSCR0_MOD) && !(ssp_read_reg(ssp, SSTSA) & 0xf)) {
+ if ((sscr0 & SSCR0_MOD) && !ttsa) {
dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
return -EINVAL;
}
@@ -800,6 +826,7 @@ static int pxa_ssp_probe(struct platform_device *pdev,
goto err_priv;
}
+ priv->dai_fmt = (unsigned int) -1;
dai->private_data = priv;
return 0;