Em Qui, 2009-06-11 às 21:34 +0800, Eric Miao escreveu:
We already use FSRT for DSP_A, and if this works on littleton I2S we
should just stick with FSRT (and frame_width = sample_width * channels)
to keep the code simple.
I hope so, but the assumption of frame_width == sample_width * 2 should
hold true first.
Ok, here is what I think that should work for I2S after my 2 patches to
sort the TDM thing.
2 Patches are inline, first version assumes that frame_width =
sample_width * 2(channels), and just increases the SFRM duration to
emulate the LRCLK.
Second version uses Eric and Paul code only for pxa3xx. In this version,
frame_width = sample_width * 2(channels) * 2(envelope).
This was only compile tested, I dont have PXA3XX hardware to test this.
It applies after the 3 patches I sent earlier.
First version:
---
sound/soc/pxa/pxa-ssp.c | 76 ++++++++++++++++-------------------------------
1 files changed, 26 insertions(+), 50 deletions(-)
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 7e72c41..631eca4 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -487,17 +487,14 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- sscr0 |= SSCR0_PSP;
- sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
- /* See hw_params() */
- break;
-
case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_I2S:
sspsp |= SSPSP_FSRT;
case SND_SOC_DAIFMT_DSP_B:
+ case SND_SOC_DAIFMT_LEFT_J:
sscr0 |= SSCR0_PSP;
sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
+ /* See hw_params() for I2S and LEFT_J */
break;
default:
@@ -565,6 +562,29 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
sscr0 |= SSCR0_FPCKE;
#endif
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ /*
+ * We can't support network mode with I2S or LEFT_J,
+ * SSPFRM is asserted only for the first slot.
+ */
+ if (frame_width == 0 || chn > 2)
+ return -EINVAL;
+
+ /*
+ * I2S and LEFT_J are stereo only, we have to send data for
+ * both channels.
+ */
+ if (chn == 1)
+ frame_width *= 2;
+
+ sspsp |= SSPSP_SFRMWDTH(frame_width / 2);
+ break;
+ default:
+ break;
+ }
+
if (frame_width > 0) {
/* Not using network mode */
if (frame_width > 16)
@@ -602,50 +622,6 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
ssp_write_reg(ssp, SSCR0, sscr0);
- switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- sspsp = ssp_read_reg(ssp, SSPSP);
-
- 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.
- *
- * The SSP values used for that are all found out by
- * trying and failing a lot; some of the registers
- * needed for that mode are only available on PXA3xx.
- */
-
-#ifdef CONFIG_PXA3xx
- if (!cpu_is_pxa3xx())
- return -EINVAL;
-
- sspsp |= SSPSP_SFRMWDTH(width * 2);
- sspsp |= SSPSP_SFRMDLY(width * 4);
- sspsp |= SSPSP_EDMYSTOP(3);
- sspsp |= SSPSP_DMYSTOP(3);
- sspsp |= SSPSP_DMYSTRT(1);
-#else
- return -EINVAL;
-#endif
- } else {
- /* The frame width is the width the LRCLK is
- * asserted for; the delay is expressed in
- * half cycle units. We need the extra cycle
- * because the data starts clocking out one BCLK
- * after LRCLK changes polarity.
- */
- sspsp |= SSPSP_SFRMWDTH(width + 1);
- sspsp |= SSPSP_SFRMDLY((width + 1) * 2);
- sspsp |= SSPSP_DMYSTRT(1);
- }
-
- ssp_write_reg(ssp, SSPSP, sspsp);
- break;
- default:
- break;
- }
-
dump_registers(ssp);
return 0;
--
tg: (99ef28c..) asoc/leftj-and-i2s (depends on: asoc/ssp-internals)
Second version:
---
arch/arm/mach-pxa/include/mach/regs-ssp.h | 14 ++--
sound/soc/pxa/pxa-ssp.c | 99 ++++++++++++++---------------
2 files changed, 56 insertions(+), 57 deletions(-)
diff --git a/arch/arm/mach-pxa/include/mach/regs-ssp.h b/arch/arm/mach-pxa/include/mach/regs-ssp.h
index 6a2ed35..060e23b 100644
--- a/arch/arm/mach-pxa/include/mach/regs-ssp.h
+++ b/arch/arm/mach-pxa/include/mach/regs-ssp.h
@@ -108,21 +108,21 @@
#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */
#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */
-#if defined(CONFIG_PXA3xx)
-#define SSPSP_EDMYSTOP(x) ((x) << 28) /* Extended Dummy Stop */
-#define SSPSP_EDMYSTRT(x) ((x) << 26) /* Extended Dummy Start */
-#endif
-
#define SSPSP_FSRT (1 << 25) /* Frame Sync Relative Timing */
-#define SSPSP_DMYSTOP(x) ((x) << 23) /* Dummy Stop */
#define SSPSP_SFRMWDTH(x) ((x) << 16) /* Serial Frame Width */
#define SSPSP_SFRMDLY(x) ((x) << 9) /* Serial Frame Delay */
-#define SSPSP_DMYSTRT(x) ((x) << 7) /* Dummy Start */
#define SSPSP_STRTDLY(x) ((x) << 4) /* Start Delay */
#define SSPSP_ETDS (1 << 3) /* End of Transfer data State */
#define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */
#define SSPSP_SCMODE(x) ((x) << 0) /* Serial Bit Rate Clock Mode */
+/* NOTE: PXA3xx extends the bit number of dummy start and stop, the macros
+ * below are compatible with PXA25x/27x as long as the parameter is within
+ * the correct limits, driver code has to take care of this.
+ */
+#define SSPSP_DMYSTRT(x) ((((x) & 3) << 7) | ((((x) >> 2) & 3) << 26))
+#define SSPSP_DMYSTOP(x) ((((x) & 3) << 23) | ((((x) >> 2) & 7) << 28))
+
#define SSACD_SCDB (1 << 3) /* SSPSYSCLK Divider Bypass */
#define SSACD_ACPS(x) ((x) << 4) /* Audio clock PLL select */
#define SSACD_ACDS(x) ((x) << 0) /* Audio clock divider select */
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 7e72c41..a14ce77 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -487,17 +487,14 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- sscr0 |= SSCR0_PSP;
- sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
- /* See hw_params() */
- break;
-
case SND_SOC_DAIFMT_DSP_A:
sspsp |= SSPSP_FSRT;
case SND_SOC_DAIFMT_DSP_B:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_I2S:
sscr0 |= SSCR0_PSP;
sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
+ /* See hw_params() for I2S and LEFT_J */
break;
default:
@@ -565,6 +562,52 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
sscr0 |= SSCR0_FPCKE;
#endif
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /*
+ * We can't support network mode with I2S or LEFT_J,
+ * SSPFRM is asserted only for the first slot.
+ */
+ if (frame_width == 0 || chn > 2)
+ return -EINVAL;
+
+ /*
+ * I2S and LEFT_J are stereo only, we have to send data for
+ * both channels.
+ */
+ if (chn == 1)
+ frame_width *= 2;
+
+ /* For pxa2xx we have to stick with FSRT */
+ if (cpu_is_pxa25x() || cpu_is_pxa27x())
+ sspsp |= SSPSP_FSRT;
+
+ /* For pxa3xx we use Paul's code */
+ if (cpu_is_pxa3xx()) {
+ /* We double the frame_width to envelope the sample */
+ frame_width *= 2;
+
+ sspsp |= SSPSP_DMYSTRT(1);
+ sspsp |= SSPSP_DMYSTOP(frame_width / 2 - width - 1);
+ sspsp |= SSPSP_SFRMWDTH(frame_width / 2);
+ }
+
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ if (frame_width == 0 || chn > 2)
+ return -EINVAL;
+
+ if (chn == 1)
+ frame_width *= 2;
+
+ /* No need to envelope the frame for LEFT_J */
+ sspsp |= SSPSP_SFRMWDTH(frame_width / 2);
+ break;
+ default:
+ break;
+ }
+
if (frame_width > 0) {
/* Not using network mode */
if (frame_width > 16)
@@ -602,50 +645,6 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
ssp_write_reg(ssp, SSCR0, sscr0);
- switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- sspsp = ssp_read_reg(ssp, SSPSP);
-
- 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.
- *
- * The SSP values used for that are all found out by
- * trying and failing a lot; some of the registers
- * needed for that mode are only available on PXA3xx.
- */
-
-#ifdef CONFIG_PXA3xx
- if (!cpu_is_pxa3xx())
- return -EINVAL;
-
- sspsp |= SSPSP_SFRMWDTH(width * 2);
- sspsp |= SSPSP_SFRMDLY(width * 4);
- sspsp |= SSPSP_EDMYSTOP(3);
- sspsp |= SSPSP_DMYSTOP(3);
- sspsp |= SSPSP_DMYSTRT(1);
-#else
- return -EINVAL;
-#endif
- } else {
- /* The frame width is the width the LRCLK is
- * asserted for; the delay is expressed in
- * half cycle units. We need the extra cycle
- * because the data starts clocking out one BCLK
- * after LRCLK changes polarity.
- */
- sspsp |= SSPSP_SFRMWDTH(width + 1);
- sspsp |= SSPSP_SFRMDLY((width + 1) * 2);
- sspsp |= SSPSP_DMYSTRT(1);
- }
-
- ssp_write_reg(ssp, SSPSP, sspsp);
- break;
- default:
- break;
- }
-
dump_registers(ssp);
return 0;
--
tg: (99ef28c..) asoc/leftj-and-i2s (depends on: asoc/ssp-internals)
--
Daniel Ribeiro