PCM channel shift/swap with SSC

This forum is for users of Microchip MPUs and who are interested in using Linux OS.

Moderator: nferre

rocq81
Posts: 1
Joined: Mon Nov 14, 2016 4:44 pm

PCM channel shift/swap with SSC

Mon Nov 14, 2016 5:12 pm

Hi All,

We're currently using the 3.6.9 kernel for one of our products running on a SAMA5D3. We have a 4-channel audio codec connected to the SSC bus, using DSP-A mode.

We are experiencing an issue where PCM data would be send in one timeslot earlier than the timeslot we would expect. To reproduce this issue:

1) Start recording
2) Start playback on any channel --> Audio playback is on correct channel
3) Stop playback (Recording is still active)
4) Start playback on any channel --> Audio playback could be (not always) on the wrong channel

(This appears to happen too when I restart recoding while playback is active)

I was able to fix this with the following patch (applied on current master):

Code: Select all

diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 633d54ca..c94cd9d 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -782,6 +782,7 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
 	struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
 	struct atmel_pcm_dma_params *dma_params;
 	int dir;
+	u32 sr;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		dir = 0;
@@ -793,9 +794,21 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
 	ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
 	ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error);
 
+	/* Prevent channel shifting */
+	sr = ssc_readl(ssc_p->ssc->regs, SR);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* Feed THR here if the transmitter is ready to send. */
+		if ( sr & SSC_BIT(SR_TXRDY))
+			ssc_writel(ssc_p->ssc->regs, THR, 0);
+	} else {
+		/* Read RHR here if the receiver is ready to read. */
+		if ( sr & SSC_BIT(SR_RXRDY))
+			ssc_readl(ssc_p->ssc->regs, RHR);
+	}
+
 	pr_debug("%s enabled SSC_SR=0x%08x\n",
 			dir ? "receive" : "transmit",
-			ssc_readl(ssc_p->ssc->regs, SR));
+			sr);
 	return 0;
 }
 
-- 
As said, we're still using linux 3.6.9 and I'm not really sure if this issue is applicable to 4.4/master. Therefore I didn't push this to github. Maybe the kernel maintainers could have a look at this.

Return to “LINUX”

Who is online

Users browsing this forum: No registered users and 27 guests