summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/fsl/Kconfig75
-rw-r--r--sound/soc/fsl/Makefile3
-rw-r--r--sound/soc/fsl/fsl_esai.c32
-rw-r--r--sound/soc/fsl/fsl_sai.c255
-rw-r--r--sound/soc/fsl/fsl_sai.h16
-rw-r--r--sound/soc/fsl/fsl_spdif.c186
-rw-r--r--sound/soc/fsl/fsl_spdif.h14
-rw-r--r--sound/soc/fsl/fsl_ssi.c996
-rw-r--r--sound/soc/fsl/fsl_ssi.h62
-rw-r--r--sound/soc/fsl/fsl_ssi_dbg.c163
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c1
-rw-r--r--sound/soc/soc-devres.c1
12 files changed, 970 insertions, 834 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index f4069d0..d262ec0 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,30 +1,77 @@
+menu "SoC Audio for Freescale CPUs"
+
+comment "Common SoC Audio options for Freescale CPUs:"
+
config SND_SOC_FSL_SAI
- tristate
+ tristate "Synchronous Audio Interface (SAI) module support"
select REGMAP_MMIO
select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y if you want to add Synchronous Audio Interface (SAI)
+ support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
config SND_SOC_FSL_SSI
- tristate
+ tristate "Synchronous Serial Interface module support"
+ select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+ select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
+ help
+ Say Y if you want to add Synchronous Serial Interface (SSI)
+ support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
config SND_SOC_FSL_SPDIF
- tristate
+ tristate "Sony/Philips Digital Interface module support"
select REGMAP_MMIO
+ select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+ select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
+ help
+ Say Y if you want to add Sony/Philips Digital Interface (SPDIF)
+ support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
config SND_SOC_FSL_ESAI
- tristate
+ tristate "Enhanced Serial Audio Interface (ESAI) module support"
select REGMAP_MMIO
select SND_SOC_FSL_UTILS
+ help
+ Say Y if you want to add Enhanced Synchronous Audio Interface
+ (ESAI) support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
config SND_SOC_FSL_UTILS
tristate
-menuconfig SND_POWERPC_SOC
+config SND_SOC_IMX_PCM_DMA
+ tristate
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+
+config SND_SOC_IMX_AUDMUX
+ tristate "Digital Audio Mux module support"
+ help
+ Say Y if you want to add Digital Audio Mux (AUDMUX) support
+ for the ARM i.MX CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
+
+config SND_POWERPC_SOC
tristate "SoC Audio for Freescale PowerPC CPUs"
depends on FSL_SOC || PPC_MPC52xx
help
Say Y or M if you want to add support for codecs attached to
the PowerPC CPUs.
+config SND_IMX_SOC
+ tristate "SoC Audio for Freescale i.MX CPUs"
+ depends on ARCH_MXC || COMPILE_TEST
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the i.MX CPUs.
+
if SND_POWERPC_SOC
config SND_MPC52xx_DMA
@@ -33,6 +80,8 @@ config SND_MPC52xx_DMA
config SND_SOC_POWERPC_DMA
tristate
+comment "SoC Audio support for Freescale PPC boards:"
+
config SND_SOC_MPC8610_HPCD
tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
# I2C is necessary for the CS4270 driver
@@ -110,13 +159,6 @@ config SND_MPC52xx_SOC_EFIKA
endif # SND_POWERPC_SOC
-menuconfig SND_IMX_SOC
- tristate "SoC Audio for Freescale i.MX CPUs"
- depends on ARCH_MXC || COMPILE_TEST
- help
- Say Y or M if you want to add support for codecs attached to
- the i.MX CPUs.
-
if SND_IMX_SOC
config SND_SOC_IMX_SSI
@@ -127,12 +169,7 @@ config SND_SOC_IMX_PCM_FIQ
tristate
select FIQ
-config SND_SOC_IMX_PCM_DMA
- tristate
- select SND_SOC_GENERIC_DMAENGINE_PCM
-
-config SND_SOC_IMX_AUDMUX
- tristate
+comment "SoC Audio support for Freescale i.MX boards:"
config SND_MXC_SOC_WM1133_EV1
tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
@@ -225,3 +262,5 @@ config SND_SOC_IMX_MC13783
select SND_SOC_IMX_PCM_DMA
endif # SND_IMX_SOC
+
+endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index b12ad4b..db254e3 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -12,7 +12,8 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
snd-soc-fsl-sai-objs := fsl_sai.o
-snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-ssi-y := fsl_ssi.o
+snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
snd-soc-fsl-spdif-objs := fsl_spdif.o
snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-utils-objs := fsl_utils.o
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 496ce2e..d719caf 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -39,6 +39,8 @@
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @hck_rate: clock rate of desired HCKx clock
+ * @sck_rate: clock rate of desired SCKx clock
+ * @hck_dir: the direction of HCKx pads
* @sck_div: if using PSR/PM dividers for SCKx clock
* @slave_mode: if fully using DAI slave mode
* @synchronous: if using tx/rx synchronous mode
@@ -55,6 +57,8 @@ struct fsl_esai {
u32 fifo_depth;
u32 slot_width;
u32 hck_rate[2];
+ u32 sck_rate[2];
+ bool hck_dir[2];
bool sck_div[2];
bool slave_mode;
bool synchronous;
@@ -209,8 +213,13 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
struct clk *clksrc = esai_priv->extalclk;
bool tx = clk_id <= ESAI_HCKT_EXTAL;
bool in = dir == SND_SOC_CLOCK_IN;
- u32 ret, ratio, ecr = 0;
+ u32 ratio, ecr = 0;
unsigned long clk_rate;
+ int ret;
+
+ /* Bypass divider settings if the requirement doesn't change */
+ if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx])
+ return 0;
/* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
esai_priv->sck_div[tx] = true;
@@ -277,6 +286,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
esai_priv->sck_div[tx] = false;
out:
+ esai_priv->hck_dir[tx] = dir;
esai_priv->hck_rate[tx] = freq;
regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
@@ -294,9 +304,10 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
u32 hck_rate = esai_priv->hck_rate[tx];
u32 sub, ratio = hck_rate / freq;
+ int ret;
- /* Don't apply for fully slave mode*/
- if (esai_priv->slave_mode)
+ /* Don't apply for fully slave mode or unchanged bclk */
+ if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq)
return 0;
if (ratio * freq > hck_rate)
@@ -319,8 +330,15 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
return -EINVAL;
}
- return fsl_esai_divisor_cal(dai, tx, ratio, true,
+ ret = fsl_esai_divisor_cal(dai, tx, ratio, true,
esai_priv->sck_div[tx] ? 0 : ratio);
+ if (ret)
+ return ret;
+
+ /* Save current bclk rate */
+ esai_priv->sck_rate[tx] = freq;
+
+ return 0;
}
static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
@@ -439,8 +457,8 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int fsl_esai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- int ret;
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
/*
* Some platforms might use the same bit to gate all three or two of
@@ -492,7 +510,8 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u32 width = snd_pcm_format_width(params_format(params));
u32 channels = params_channels(params);
- u32 bclk, mask, val, ret;
+ u32 bclk, mask, val;
+ int ret;
bclk = params_rate(params) * esai_priv->slot_width * 2;
@@ -822,6 +841,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
static const struct of_device_id fsl_esai_dt_ids[] = {
{ .compatible = "fsl,imx35-esai", },
+ { .compatible = "fsl,vf610-esai", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 56da8c8..c5a0e8a 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -22,6 +22,7 @@
#include <sound/pcm_params.h>
#include "fsl_sai.h"
+#include "imx-pcm.h"
#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
FSL_SAI_CSR_FEIE)
@@ -30,78 +31,96 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
{
struct fsl_sai *sai = (struct fsl_sai *)devid;
struct device *dev = &sai->pdev->dev;
- u32 xcsr, mask;
+ u32 flags, xcsr, mask;
+ bool irq_none = true;
- /* Only handle those what we enabled */
+ /*
+ * Both IRQ status bits and IRQ mask bits are in the xCSR but
+ * different shifts. And we here create a mask only for those
+ * IRQs that we activated.
+ */
mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
/* Tx IRQ */
regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr);
- xcsr &= mask;
+ flags = xcsr & mask;
+
+ if (flags)
+ irq_none = false;
+ else
+ goto irq_rx;
- if (xcsr & FSL_SAI_CSR_WSF)
+ if (flags & FSL_SAI_CSR_WSF)
dev_dbg(dev, "isr: Start of Tx word detected\n");
- if (xcsr & FSL_SAI_CSR_SEF)
+ if (flags & FSL_SAI_CSR_SEF)
dev_warn(dev, "isr: Tx Frame sync error detected\n");
- if (xcsr & FSL_SAI_CSR_FEF) {
+ if (flags & FSL_SAI_CSR_FEF) {
dev_warn(dev, "isr: Transmit underrun detected\n");
/* FIFO reset for safety */
xcsr |= FSL_SAI_CSR_FR;
}
- if (xcsr & FSL_SAI_CSR_FWF)
+ if (flags & FSL_SAI_CSR_FWF)
dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n");
- if (xcsr & FSL_SAI_CSR_FRF)
+ if (flags & FSL_SAI_CSR_FRF)
dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n");
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
- FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
+ flags &= FSL_SAI_CSR_xF_W_MASK;
+ xcsr &= ~FSL_SAI_CSR_xF_MASK;
+
+ if (flags)
+ regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
+irq_rx:
/* Rx IRQ */
regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr);
- xcsr &= mask;
+ flags = xcsr & mask;
- if (xcsr & FSL_SAI_CSR_WSF)
+ if (flags)
+ irq_none = false;
+ else
+ goto out;
+
+ if (flags & FSL_SAI_CSR_WSF)
dev_dbg(dev, "isr: Start of Rx word detected\n");
- if (xcsr & FSL_SAI_CSR_SEF)
+ if (flags & FSL_SAI_CSR_SEF)
dev_warn(dev, "isr: Rx Frame sync error detected\n");
- if (xcsr & FSL_SAI_CSR_FEF) {
+ if (flags & FSL_SAI_CSR_FEF) {
dev_warn(dev, "isr: Receive overflow detected\n");
/* FIFO reset for safety */
xcsr |= FSL_SAI_CSR_FR;
}
- if (xcsr & FSL_SAI_CSR_FWF)
+ if (flags & FSL_SAI_CSR_FWF)
dev_dbg(dev, "isr: Enabled receive FIFO is full\n");
- if (xcsr & FSL_SAI_CSR_FRF)
+ if (flags & FSL_SAI_CSR_FRF)
dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n");
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
- FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
+ flags &= FSL_SAI_CSR_xF_W_MASK;
+ xcsr &= ~FSL_SAI_CSR_xF_MASK;
+
+ if (flags)
+ regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
- return IRQ_HANDLED;
+out:
+ if (irq_none)
+ return IRQ_NONE;
+ else
+ return IRQ_HANDLED;
}
static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int fsl_dir)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
- u32 val_cr2, reg_cr2;
-
- if (fsl_dir == FSL_FMT_TRANSMITTER)
- reg_cr2 = FSL_SAI_TCR2;
- else
- reg_cr2 = FSL_SAI_RCR2;
-
- regmap_read(sai->regmap, reg_cr2, &val_cr2);
-
- val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
+ bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
+ u32 val_cr2 = 0;
switch (clk_id) {
case FSL_SAI_CLK_BUS:
@@ -120,7 +139,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- regmap_write(sai->regmap, reg_cr2, val_cr2);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+ FSL_SAI_CR2_MSEL_MASK, val_cr2);
return 0;
}
@@ -152,22 +172,10 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
unsigned int fmt, int fsl_dir)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
- u32 val_cr2, val_cr4, reg_cr2, reg_cr4;
-
- if (fsl_dir == FSL_FMT_TRANSMITTER) {
- reg_cr2 = FSL_SAI_TCR2;
- reg_cr4 = FSL_SAI_TCR4;
- } else {
- reg_cr2 = FSL_SAI_RCR2;
- reg_cr4 = FSL_SAI_RCR4;
- }
+ bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
+ u32 val_cr2 = 0, val_cr4 = 0;
- regmap_read(sai->regmap, reg_cr2, &val_cr2);
- regmap_read(sai->regmap, reg_cr4, &val_cr4);
-
- if (sai->big_endian_data)
- val_cr4 &= ~FSL_SAI_CR4_MF;
- else
+ if (!sai->big_endian_data)
val_cr4 |= FSL_SAI_CR4_MF;
/* DAI mode */
@@ -188,7 +196,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
* frame sync asserts with the first bit of the frame.
*/
val_cr2 |= FSL_SAI_CR2_BCP;
- val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
break;
case SND_SOC_DAIFMT_DSP_A:
/*
@@ -198,7 +205,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
* data word.
*/
val_cr2 |= FSL_SAI_CR2_BCP;
- val_cr4 &= ~FSL_SAI_CR4_FSP;
val_cr4 |= FSL_SAI_CR4_FSE;
sai->is_dsp_mode = true;
break;
@@ -208,7 +214,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
* frame sync asserts with the first bit of the frame.
*/
val_cr2 |= FSL_SAI_CR2_BCP;
- val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
sai->is_dsp_mode = true;
break;
case SND_SOC_DAIFMT_RIGHT_J:
@@ -246,23 +251,22 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
break;
case SND_SOC_DAIFMT_CBM_CFM:
- val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
- val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
break;
case SND_SOC_DAIFMT_CBS_CFM:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
- val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
break;
case SND_SOC_DAIFMT_CBM_CFS:
- val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
break;
default:
return -EINVAL;
}
- regmap_write(sai->regmap, reg_cr2, val_cr2);
- regmap_write(sai->regmap, reg_cr4, val_cr4);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+ FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR, val_cr2);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
+ FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE |
+ FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR, val_cr4);
return 0;
}
@@ -289,29 +293,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
- u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int channels = params_channels(params);
u32 word_width = snd_pcm_format_width(params_format(params));
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- reg_cr4 = FSL_SAI_TCR4;
- reg_cr5 = FSL_SAI_TCR5;
- reg_mr = FSL_SAI_TMR;
- } else {
- reg_cr4 = FSL_SAI_RCR4;
- reg_cr5 = FSL_SAI_RCR5;
- reg_mr = FSL_SAI_RMR;
- }
-
- regmap_read(sai->regmap, reg_cr4, &val_cr4);
- regmap_read(sai->regmap, reg_cr4, &val_cr5);
-
- val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK;
- val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK;
-
- val_cr5 &= ~FSL_SAI_CR5_WNW_MASK;
- val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
- val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
+ u32 val_cr4 = 0, val_cr5 = 0;
if (!sai->is_dsp_mode)
val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
@@ -319,18 +304,20 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
val_cr5 |= FSL_SAI_CR5_WNW(word_width);
val_cr5 |= FSL_SAI_CR5_W0W(word_width);
- val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
if (sai->big_endian_data)
val_cr5 |= FSL_SAI_CR5_FBT(0);
else
val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
- val_mr = ~0UL - ((1 << channels) - 1);
- regmap_write(sai->regmap, reg_cr4, val_cr4);
- regmap_write(sai->regmap, reg_cr5, val_cr5);
- regmap_write(sai->regmap, reg_mr, val_mr);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
+ FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+ val_cr4);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx),
+ FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+ FSL_SAI_CR5_FBT_MASK, val_cr5);
+ regmap_write(sai->regmap, FSL_SAI_xMR(tx), ~0UL - ((1 << channels) - 1));
return 0;
}
@@ -339,6 +326,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u32 tcsr, rcsr;
/*
@@ -353,14 +341,6 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr);
regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- tcsr |= FSL_SAI_CSR_FRDE;
- rcsr &= ~FSL_SAI_CSR_FRDE;
- } else {
- rcsr |= FSL_SAI_CSR_FRDE;
- tcsr &= ~FSL_SAI_CSR_FRDE;
- }
-
/*
* It is recommended that the transmitter is the last enabled
* and the first disabled.
@@ -369,22 +349,33 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- tcsr |= FSL_SAI_CSR_TERE;
- rcsr |= FSL_SAI_CSR_TERE;
+ if (!(tcsr & FSL_SAI_CSR_FRDE || rcsr & FSL_SAI_CSR_FRDE)) {
+ regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+ FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+ FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+ }
- regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
- regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (!(cpu_dai->playback_active || cpu_dai->capture_active)) {
- tcsr &= ~FSL_SAI_CSR_TERE;
- rcsr &= ~FSL_SAI_CSR_TERE;
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ FSL_SAI_CSR_FRDE, 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ FSL_SAI_CSR_xIE_MASK, 0);
+
+ /* Check if the opposite FRDE is also disabled */
+ if (!(tx ? rcsr & FSL_SAI_CSR_FRDE : tcsr & FSL_SAI_CSR_FRDE)) {
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+ FSL_SAI_CSR_TERE, 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+ FSL_SAI_CSR_TERE, 0);
}
-
- regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
- regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
break;
default:
return -EINVAL;
@@ -397,14 +388,17 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
- u32 reg;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct device *dev = &sai->pdev->dev;
+ int ret;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- reg = FSL_SAI_TCR3;
- else
- reg = FSL_SAI_RCR3;
+ ret = clk_prepare_enable(sai->bus_clk);
+ if (ret) {
+ dev_err(dev, "failed to enable bus clock: %d\n", ret);
+ return ret;
+ }
- regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE,
FSL_SAI_CR3_TRCE);
return 0;
@@ -414,15 +408,11 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
- u32 reg;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- reg = FSL_SAI_TCR3;
- else
- reg = FSL_SAI_RCR3;
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, 0);
- regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
- ~FSL_SAI_CR3_TRCE);
+ clk_disable_unprepare(sai->bus_clk);
}
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
@@ -438,8 +428,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, FSL_SAI_FLAGS);
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, FSL_SAI_FLAGS);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0);
regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
FSL_SAI_MAXBURST_TX * 2);
regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
@@ -555,7 +545,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
struct fsl_sai *sai;
struct resource *res;
void __iomem *base;
- int irq, ret;
+ char tmp[8];
+ int irq, ret, i;
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
if (!sai)
@@ -563,6 +554,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
sai->pdev = pdev;
+ if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
+ sai->sai_on_imx = true;
+
sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
if (sai->big_endian_regs)
fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
@@ -575,12 +569,35 @@ static int fsl_sai_probe(struct platform_device *pdev)
return PTR_ERR(base);
sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
- "sai", base, &fsl_sai_regmap_config);
+ "bus", base, &fsl_sai_regmap_config);
+
+ /* Compatible with old DTB cases */
+ if (IS_ERR(sai->regmap))
+ sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ "sai", base, &fsl_sai_regmap_config);
if (IS_ERR(sai->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
return PTR_ERR(sai->regmap);
}
+ /* No error out for old DTB cases but only mark the clock NULL */
+ sai->bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(sai->bus_clk)) {
+ dev_err(&pdev->dev, "failed to get bus clock: %ld\n",
+ PTR_ERR(sai->bus_clk));
+ sai->bus_clk = NULL;
+ }
+
+ for (i = 0; i < FSL_SAI_MCLK_MAX; i++) {
+ sprintf(tmp, "mclk%d", i + 1);
+ sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
+ if (IS_ERR(sai->mclk_clk[i])) {
+ dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
+ i + 1, PTR_ERR(sai->mclk_clk[i]));
+ sai->mclk_clk[i] = NULL;
+ }
+ }
+
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
@@ -605,12 +622,16 @@ static int fsl_sai_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
- SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+ if (sai->sai_on_imx)
+ return imx_pcm_dma_init(pdev);
+ else
+ return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
}
static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,vf610-sai", },
+ { .compatible = "fsl,imx6sx-sai", },
{ /* sentinel */ }
};
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index a264185..0e6c9f5 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -35,6 +35,16 @@
#define FSL_SAI_RFR 0xc0 /* SAI Receive FIFO */
#define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */
+#define FSL_SAI_xCSR(tx) (tx ? FSL_SAI_TCSR : FSL_SAI_RCSR)
+#define FSL_SAI_xCR1(tx) (tx ? FSL_SAI_TCR1 : FSL_SAI_RCR1)
+#define FSL_SAI_xCR2(tx) (tx ? FSL_SAI_TCR2 : FSL_SAI_RCR2)
+#define FSL_SAI_xCR3(tx) (tx ? FSL_SAI_TCR3 : FSL_SAI_RCR3)
+#define FSL_SAI_xCR4(tx) (tx ? FSL_SAI_TCR4 : FSL_SAI_RCR4)
+#define FSL_SAI_xCR5(tx) (tx ? FSL_SAI_TCR5 : FSL_SAI_RCR5)
+#define FSL_SAI_xDR(tx) (tx ? FSL_SAI_TDR : FSL_SAI_RDR)
+#define FSL_SAI_xFR(tx) (tx ? FSL_SAI_TFR : FSL_SAI_RFR)
+#define FSL_SAI_xMR(tx) (tx ? FSL_SAI_TMR : FSL_SAI_RMR)
+
/* SAI Transmit/Recieve Control Register */
#define FSL_SAI_CSR_TERE BIT(31)
#define FSL_SAI_CSR_FR BIT(25)
@@ -48,6 +58,7 @@
#define FSL_SAI_CSR_FWF BIT(17)
#define FSL_SAI_CSR_FRF BIT(16)
#define FSL_SAI_CSR_xIE_SHIFT 8
+#define FSL_SAI_CSR_xIE_MASK (0x1f << FSL_SAI_CSR_xIE_SHIFT)
#define FSL_SAI_CSR_WSIE BIT(12)
#define FSL_SAI_CSR_SEIE BIT(11)
#define FSL_SAI_CSR_FEIE BIT(10)
@@ -108,6 +119,8 @@
#define FSL_SAI_CLK_MAST2 2
#define FSL_SAI_CLK_MAST3 3
+#define FSL_SAI_MCLK_MAX 3
+
/* SAI data transfer numbers per DMA request */
#define FSL_SAI_MAXBURST_TX 6
#define FSL_SAI_MAXBURST_RX 6
@@ -115,10 +128,13 @@
struct fsl_sai {
struct platform_device *pdev;
struct regmap *regmap;
+ struct clk *bus_clk;
+ struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
bool big_endian_regs;
bool big_endian_data;
bool is_dsp_mode;
+ bool sai_on_imx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx;
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 6452ca8..b912d45 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -13,18 +13,18 @@
* kind, whether express or implied.
*/
-#include <linux/module.h>
+#include <linux/bitrev.h>
#include <linux/clk.h>
#include <linux/clk-private.h>
-#include <linux/bitrev.h>
-#include <linux/regmap.h>
+#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
+#include <linux/regmap.h>
#include <sound/asoundef.h>
-#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
#include "fsl_spdif.h"
#include "imx-pcm.h"
@@ -69,17 +69,42 @@ struct spdif_mixer_control {
u32 ready_buf;
};
+/**
+ * fsl_spdif_priv: Freescale SPDIF private data
+ *
+ * @fsl_spdif_control: SPDIF control data
+ * @cpu_dai_drv: cpu dai driver
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @dpll_locked: dpll lock flag
+ * @txrate: the best rates for playback
+ * @txclk_df: STC_TXCLK_DF dividers value for playback
+ * @sysclk_df: STC_SYSCLK_DF dividers value for playback
+ * @txclk_src: STC_TXCLK_SRC values for playback
+ * @rxclk_src: SRPC_CLKSRC_SEL values for capture
+ * @txclk: tx clock sources for playback
+ * @rxclk: rx clock sources for capture
+ * @coreclk: core clock for register access via DMA
+ * @sysclk: system clock for rx clock rate measurement
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @dma_params_rx: DMA parameters for receive channel
+ * @name: driver name
+ */
struct fsl_spdif_priv {
struct spdif_mixer_control fsl_spdif_control;
struct snd_soc_dai_driver cpu_dai_drv;
struct platform_device *pdev;
struct regmap *regmap;
bool dpll_locked;
- u8 txclk_div[SPDIF_TXRATE_MAX];
+ u16 txrate[SPDIF_TXRATE_MAX];
+ u8 txclk_df[SPDIF_TXRATE_MAX];
+ u8 sysclk_df[SPDIF_TXRATE_MAX];
u8 txclk_src[SPDIF_TXRATE_MAX];
u8 rxclk_src;
struct clk *txclk[SPDIF_TXRATE_MAX];
struct clk *rxclk;
+ struct clk *coreclk;
+ struct clk *sysclk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
@@ -349,7 +374,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
struct platform_device *pdev = spdif_priv->pdev;
unsigned long csfs = 0;
u32 stc, mask, rate;
- u8 clk, div;
+ u8 clk, txclk_df, sysclk_df;
int ret;
switch (sample_rate) {
@@ -376,25 +401,31 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
return -EINVAL;
}
- div = spdif_priv->txclk_div[rate];
- if (div == 0) {
- dev_err(&pdev->dev, "the divisor can't be zero\n");
+ txclk_df = spdif_priv->txclk_df[rate];
+ if (txclk_df == 0) {
+ dev_err(&pdev->dev, "the txclk_df can't be zero\n");
return -EINVAL;
}
+ sysclk_df = spdif_priv->sysclk_df[rate];
+
+ /* Don't mess up the clocks from other modules */
+ if (clk != STC_TXCLK_SPDIF_ROOT)
+ goto clk_set_bypass;
+
/*
- * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block
- * will divide by (div). So request 64 * fs * (div+1) which will
- * get rounded.
+ * The S/PDIF block needs a clock of 64 * fs * txclk_df.
+ * So request 64 * fs * (txclk_df + 1) to get rounded.
*/
- ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
+ ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1));
if (ret) {
dev_err(&pdev->dev, "failed to set tx clock rate\n");
return ret;
}
+clk_set_bypass:
dev_dbg(&pdev->dev, "expected clock rate = %d\n",
- (64 * sample_rate * div));
+ (64 * sample_rate * txclk_df * sysclk_df));
dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
clk_get_rate(spdif_priv->txclk[rate]));
@@ -402,11 +433,15 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
/* select clock source and divisor */
- stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
- mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
+ stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DF(txclk_df);
+ mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK;
regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
- dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
+ regmap_update_bits(regmap, REG_SPDIF_STC,
+ STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df));
+
+ dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n",
+ spdif_priv->txrate[rate], sample_rate);
return 0;
}
@@ -423,10 +458,16 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
/* Reset module and interrupts only for first initialization */
if (!cpu_dai->active) {
+ ret = clk_prepare_enable(spdif_priv->coreclk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable core clock\n");
+ return ret;
+ }
+
ret = spdif_softreset(spdif_priv);
if (ret) {
dev_err(&pdev->dev, "failed to soft reset\n");
- return ret;
+ goto err;
}
/* Disable all the interrupts */
@@ -454,6 +495,11 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
return 0;
+
+err:
+ clk_disable_unprepare(spdif_priv->coreclk);
+
+ return ret;
}
static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
@@ -484,6 +530,7 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
spdif_intr_status_clear(spdif_priv);
regmap_update_bits(regmap, REG_SPDIF_SCR,
SCR_LOW_POWER, SCR_LOW_POWER);
+ clk_disable_unprepare(spdif_priv->coreclk);
}
}
@@ -754,7 +801,7 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
/* Get bus clock from system */
- busclk_freq = clk_get_rate(spdif_priv->rxclk);
+ busclk_freq = clk_get_rate(spdif_priv->sysclk);
}
/* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
@@ -997,43 +1044,61 @@ static struct regmap_config fsl_spdif_regmap_config = {
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
struct clk *clk, u64 savesub,
- enum spdif_txrate index)
+ enum spdif_txrate index, bool round)
{
const u32 rate[] = { 32000, 44100, 48000 };
+ bool is_sysclk = clk == spdif_priv->sysclk;
u64 rate_ideal, rate_actual, sub;
- u32 div, arate;
-
- for (div = 1; div <= 128; div++) {
- rate_ideal = rate[index] * (div + 1) * 64;
- rate_actual = clk_round_rate(clk, rate_ideal);
-
- arate = rate_actual / 64;
- arate /= div;
-
- if (arate == rate[index]) {
- /* We are lucky */
- savesub = 0;
- spdif_priv->txclk_div[index] = div;
- break;
- } else if (arate / rate[index] == 1) {
- /* A little bigger than expect */
- sub = (arate - rate[index]) * 100000;
- do_div(sub, rate[index]);
- if (sub < savesub) {
+ u32 sysclk_dfmin, sysclk_dfmax;
+ u32 txclk_df, sysclk_df, arate;
+
+ /* The sysclk has an extra divisor [2, 512] */
+ sysclk_dfmin = is_sysclk ? 2 : 1;
+ sysclk_dfmax = is_sysclk ? 512 : 1;
+
+ for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
+ for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
+ rate_ideal = rate[index] * (txclk_df + 1) * 64;
+ if (round)
+ rate_actual = clk_round_rate(clk, rate_ideal);
+ else
+ rate_actual = clk_get_rate(clk);
+
+ arate = rate_actual / 64;
+ arate /= txclk_df * sysclk_df;
+
+ if (arate == rate[index]) {
+ /* We are lucky */
+ savesub = 0;
+ spdif_priv->txclk_df[index] = txclk_df;
+ spdif_priv->sysclk_df[index] = sysclk_df;
+ spdif_priv->txrate[index] = arate;
+ goto out;
+ } else if (arate / rate[index] == 1) {
+ /* A little bigger than expect */
+ sub = (arate - rate[index]) * 100000;
+ do_div(sub, rate[index]);
+ if (sub >= savesub)
+ continue;
savesub = sub;
- spdif_priv->txclk_div[index] = div;
- }
- } else if (rate[index] / arate == 1) {
- /* A little smaller than expect */
- sub = (rate[index] - arate) * 100000;
- do_div(sub, rate[index]);
- if (sub < savesub) {
+ spdif_priv->txclk_df[index] = txclk_df;
+ spdif_priv->sysclk_df[index] = sysclk_df;
+ spdif_priv->txrate[index] = arate;
+ } else if (rate[index] / arate == 1) {
+ /* A little smaller than expect */
+ sub = (rate[index] - arate) * 100000;
+ do_div(sub, rate[index]);
+ if (sub >= savesub)
+ continue;
savesub = sub;
- spdif_priv->txclk_div[index] = div;
+ spdif_priv->txclk_df[index] = txclk_df;
+ spdif_priv->sysclk_df[index] = sysclk_df;
+ spdif_priv->txrate[index] = arate;
}
}
}
+out:
return savesub;
}
@@ -1058,7 +1123,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
if (!clk_get_rate(clk))
continue;
- ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
+ ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
+ i == STC_TXCLK_SPDIF_ROOT);
if (savesub == ret)
continue;
@@ -1073,8 +1139,13 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
spdif_priv->txclk_src[index], rate[index]);
- dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n",
- spdif_priv->txclk_div[index], rate[index]);
+ dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
+ spdif_priv->txclk_df[index], rate[index]);
+ if (spdif_priv->txclk[index] == spdif_priv->sysclk)
+ dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
+ spdif_priv->sysclk_df[index], rate[index]);
+ dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
+ rate[index], spdif_priv->txrate[index]);
return 0;
}
@@ -1134,6 +1205,20 @@ static int fsl_spdif_probe(struct platform_device *pdev)
return ret;
}
+ /* Get system clock for rx clock rate calculation */
+ spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5");
+ if (IS_ERR(spdif_priv->sysclk)) {
+ dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n");
+ return PTR_ERR(spdif_priv->sysclk);
+ }
+
+ /* Get core clock for data register access via DMA */
+ spdif_priv->coreclk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(spdif_priv->coreclk)) {
+ dev_err(&pdev->dev, "no core clock in devicetree\n");
+ return PTR_ERR(spdif_priv->coreclk);
+ }
+
/* Select clock source for rx/tx clock */
spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
if (IS_ERR(spdif_priv->rxclk)) {
@@ -1186,6 +1271,7 @@ static int fsl_spdif_probe(struct platform_device *pdev)
static const struct of_device_id fsl_spdif_dt_ids[] = {
{ .compatible = "fsl,imx35-spdif", },
+ { .compatible = "fsl,vf610-spdif", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
index 605a10b..16fde4b 100644
--- a/sound/soc/fsl/fsl_spdif.h
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -143,20 +143,22 @@ enum spdif_gainsel {
#define INT_RXFIFO_FUL (1 << 0)
/* SPDIF Clock register */
-#define STC_SYSCLK_DIV_OFFSET 11
-#define STC_SYSCLK_DIV_MASK (0x1ff << STC_SYSCLK_DIV_OFFSET)
-#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_SYSCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
+#define STC_SYSCLK_DF_OFFSET 11
+#define STC_SYSCLK_DF_MASK (0x1ff << STC_SYSCLK_DF_OFFSET)
+#define STC_SYSCLK_DF(x) ((((x) - 1) << STC_SYSCLK_DF_OFFSET) & STC_SYSCLK_DF_MASK)
#define STC_TXCLK_SRC_OFFSET 8
#define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET)
#define STC_TXCLK_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
#define STC_TXCLK_ALL_EN_OFFSET 7
#define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET)
#define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET)
-#define STC_TXCLK_DIV_OFFSET 0
-#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET)
-#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
+#define STC_TXCLK_DF_OFFSET 0
+#define STC_TXCLK_DF_MASK (0x7ff << STC_TXCLK_DF_OFFSET)
+#define STC_TXCLK_DF(x) ((((x) - 1) << STC_TXCLK_DF_OFFSET) & STC_TXCLK_DF_MASK)
#define STC_TXCLK_SRC_MAX 8
+#define STC_TXCLK_SPDIF_ROOT 1
+
/* SPDIF tx rate */
enum spdif_txrate {
SPDIF_TXRATE_32000 = 0,
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 5428a1f..f233d91 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -35,11 +35,11 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
-#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
@@ -113,8 +113,6 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
#define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \
CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \
CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN)
-#define FSLSSI_SISR_MASK (FSLSSI_SIER_DBG_RX_FLAGS | FSLSSI_SIER_DBG_TX_FLAGS)
-
enum fsl_ssi_type {
FSL_SSI_MCP8610,
@@ -146,7 +144,6 @@ struct fsl_ssi_rxtx_reg_val {
* @cpu_dai: the CPU DAI for this device
* @dev_attr: the sysfs device attribute structure
* @stats: SSI statistics
- * @name: name for this device
*/
struct fsl_ssi_private {
struct ccsr_ssi __iomem *ssi;
@@ -155,15 +152,11 @@ struct fsl_ssi_private {
unsigned int fifo_depth;
struct snd_soc_dai_driver cpu_dai_drv;
struct platform_device *pdev;
+ unsigned int dai_fmt;
enum fsl_ssi_type hw_type;
- bool new_binding;
- bool ssi_on_imx;
- bool imx_ac97;
bool use_dma;
bool baudclk_locked;
- bool irq_stats;
- bool offline_config;
bool use_dual_fifo;
u8 i2s_mode;
spinlock_t baudclk_lock;
@@ -171,39 +164,11 @@ struct fsl_ssi_private {
struct clk *clk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
- struct imx_dma_data filter_data_tx;
- struct imx_dma_data filter_data_rx;
struct imx_pcm_fiq_params fiq_params;
/* Register values for rx/tx configuration */
struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
- struct {
- unsigned int rfrc;
- unsigned int tfrc;
- unsigned int cmdau;
- unsigned int cmddu;
- unsigned int rxt;
- unsigned int rdr1;
- unsigned int rdr0;
- unsigned int tde1;
- unsigned int tde0;
- unsigned int roe1;
- unsigned int roe0;
- unsigned int tue1;
- unsigned int tue0;
- unsigned int tfs;
- unsigned int rfs;
- unsigned int tls;
- unsigned int rls;
- unsigned int rff1;
- unsigned int rff0;
- unsigned int tfe1;
- unsigned int tfe0;
- } stats;
- struct dentry *dbg_dir;
- struct dentry *dbg_stats;
-
- char name[1];
+ struct fsl_ssi_dbg dbg_stats;
};
static const struct of_device_id fsl_ssi_ids[] = {
@@ -215,6 +180,54 @@ static const struct of_device_id fsl_ssi_ids[] = {
};
MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
+static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private)
+{
+ return !!(ssi_private->dai_fmt & SND_SOC_DAIFMT_AC97);
+}
+
+static bool fsl_ssi_on_imx(struct fsl_ssi_private *ssi_private)
+{
+ switch (ssi_private->hw_type) {
+ case FSL_SSI_MX21:
+ case FSL_SSI_MX35:
+ case FSL_SSI_MX51:
+ return true;
+ case FSL_SSI_MCP8610:
+ return false;
+ }
+
+ return false;
+}
+
+/*
+ * imx51 and later SoCs have a slightly different IP that allows the
+ * SSI configuration while the SSI unit is running.
+ *
+ * More important, it is necessary on those SoCs to configure the
+ * sperate TX/RX DMA bits just before starting the stream
+ * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi
+ * sends any DMA requests to the SDMA unit, otherwise it is not defined
+ * how the SDMA unit handles the DMA request.
+ *
+ * SDMA units are present on devices starting at imx35 but the imx35
+ * reference manual states that the DMA bits should not be changed
+ * while the SSI unit is running (SSIEN). So we support the necessary
+ * online configuration of fsl-ssi starting at imx51.
+ */
+static bool fsl_ssi_offline_config(struct fsl_ssi_private *ssi_private)
+{
+ switch (ssi_private->hw_type) {
+ case FSL_SSI_MCP8610:
+ case FSL_SSI_MX21:
+ case FSL_SSI_MX35:
+ return true;
+ case FSL_SSI_MX51:
+ return false;
+ }
+
+ return true;
+}
+
/**
* fsl_ssi_isr: SSI interrupt handler
*
@@ -231,7 +244,6 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
{
struct fsl_ssi_private *ssi_private = dev_id;
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
- irqreturn_t ret = IRQ_NONE;
__be32 sisr;
__be32 sisr2;
__be32 sisr_write_mask = 0;
@@ -258,217 +270,18 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
were interrupted for. We mask it with the Interrupt Enable register
so that we only check for events that we're interested in.
*/
- sisr = read_ssi(&ssi->sisr) & FSLSSI_SISR_MASK;
-
- if (sisr & CCSR_SSI_SISR_RFRC) {
- ssi_private->stats.rfrc++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TFRC) {
- ssi_private->stats.tfrc++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_CMDAU) {
- ssi_private->stats.cmdau++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_CMDDU) {
- ssi_private->stats.cmddu++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RXT) {
- ssi_private->stats.rxt++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RDR1) {
- ssi_private->stats.rdr1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RDR0) {
- ssi_private->stats.rdr0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TDE1) {
- ssi_private->stats.tde1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TDE0) {
- ssi_private->stats.tde0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_ROE1) {
- ssi_private->stats.roe1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_ROE0) {
- ssi_private->stats.roe0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TUE1) {
- ssi_private->stats.tue1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TUE0) {
- ssi_private->stats.tue0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TFS) {
- ssi_private->stats.tfs++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RFS) {
- ssi_private->stats.rfs++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TLS) {
- ssi_private->stats.tls++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RLS) {
- ssi_private->stats.rls++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RFF1) {
- ssi_private->stats.rff1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RFF0) {
- ssi_private->stats.rff0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TFE1) {
- ssi_private->stats.tfe1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TFE0) {
- ssi_private->stats.tfe0++;
- ret = IRQ_HANDLED;
- }
+ sisr = read_ssi(&ssi->sisr);
sisr2 = sisr & sisr_write_mask;
/* Clear the bits that we set */
if (sisr2)
write_ssi(sisr2, &ssi->sisr);
- return ret;
-}
-
-#if IS_ENABLED(CONFIG_DEBUG_FS)
-/* Show the statistics of a flag only if its interrupt is enabled. The
- * compiler will optimze this code to a no-op if the interrupt is not
- * enabled.
- */
-#define SIER_SHOW(flag, name) \
- do { \
- if (FSLSSI_SISR_MASK & CCSR_SSI_SIER_##flag) \
- seq_printf(s, #name "=%u\n", ssi_private->stats.name); \
- } while (0)
-
-
-/**
- * fsl_sysfs_ssi_show: display SSI statistics
- *
- * Display the statistics for the current SSI device. To avoid confusion,
- * we only show those counts that are enabled.
- */
-static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
-{
- struct fsl_ssi_private *ssi_private = s->private;
-
- SIER_SHOW(RFRC_EN, rfrc);
- SIER_SHOW(TFRC_EN, tfrc);
- SIER_SHOW(CMDAU_EN, cmdau);
- SIER_SHOW(CMDDU_EN, cmddu);
- SIER_SHOW(RXT_EN, rxt);
- SIER_SHOW(RDR1_EN, rdr1);
- SIER_SHOW(RDR0_EN, rdr0);
- SIER_SHOW(TDE1_EN, tde1);
- SIER_SHOW(TDE0_EN, tde0);
- SIER_SHOW(ROE1_EN, roe1);
- SIER_SHOW(ROE0_EN, roe0);
- SIER_SHOW(TUE1_EN, tue1);
- SIER_SHOW(TUE0_EN, tue0);
- SIER_SHOW(TFS_EN, tfs);
- SIER_SHOW(RFS_EN, rfs);
- SIER_SHOW(TLS_EN, tls);
- SIER_SHOW(RLS_EN, rls);
- SIER_SHOW(RFF1_EN, rff1);
- SIER_SHOW(RFF0_EN, rff0);
- SIER_SHOW(TFE1_EN, tfe1);
- SIER_SHOW(TFE0_EN, tfe0);
+ fsl_ssi_dbg_isr(&ssi_private->dbg_stats, sisr);
- return 0;
+ return IRQ_HANDLED;
}
-static int fsl_ssi_stats_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fsl_ssi_stats_show, inode->i_private);
-}
-
-static const struct file_operations fsl_ssi_stats_ops = {
- .open = fsl_ssi_stats_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
- struct device *dev)
-{
- ssi_private->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
- if (!ssi_private->dbg_dir)
- return -ENOMEM;
-
- ssi_private->dbg_stats = debugfs_create_file("stats", S_IRUGO,
- ssi_private->dbg_dir, ssi_private, &fsl_ssi_stats_ops);
- if (!ssi_private->dbg_stats) {
- debugfs_remove(ssi_private->dbg_dir);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
-{
- debugfs_remove(ssi_private->dbg_stats);
- debugfs_remove(ssi_private->dbg_dir);
-}
-
-#else
-
-static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
- struct device *dev)
-{
- return 0;
-}
-
-static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
-{
-}
-
-#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
-
/*
* Enable/Disable all rx/tx config flags at once.
*/
@@ -490,6 +303,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
}
/*
+ * Calculate the bits that have to be disabled for the current stream that is
+ * getting disabled. This keeps the bits enabled that are necessary for the
+ * second stream to work if 'stream_active' is true.
+ *
+ * Detailed calculation:
+ * These are the values that need to be active after disabling. For non-active
+ * second stream, this is 0:
+ * vals_stream * !!stream_active
+ *
+ * The following computes the overall differences between the setup for the
+ * to-disable stream and the active stream, a simple XOR:
+ * vals_disable ^ (vals_stream * !!(stream_active))
+ *
+ * The full expression adds a mask on all values we care about
+ */
+#define fsl_ssi_disable_val(vals_disable, vals_stream, stream_active) \
+ ((vals_disable) & \
+ ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active))))
+
+/*
* Enable/Disable a ssi configuration. You have to pass either
* ssi_private->rxtx_reg_val.rx or tx as vals parameter.
*/
@@ -501,6 +334,12 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
u32 scr_val = read_ssi(&ssi->scr);
int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
!!(scr_val & CCSR_SSI_SCR_RE);
+ int keep_active;
+
+ if (nr_active_streams - 1 > 0)
+ keep_active = 1;
+ else
+ keep_active = 0;
/* Find the other direction values rx or tx which we do not want to
* modify */
@@ -511,7 +350,8 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
/* If vals should be disabled, start with disabling the unit */
if (!enable) {
- u32 scr = vals->scr & (vals->scr ^ avals->scr);
+ u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr,
+ keep_active);
write_ssi_mask(&ssi->scr, scr, 0);
}
@@ -520,9 +360,9 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
* reconfiguration, so we have to enable all necessary flags at once
* even if we do not use them later (capture and playback configuration)
*/
- if (ssi_private->offline_config) {
+ if (fsl_ssi_offline_config(ssi_private)) {
if ((enable && !nr_active_streams) ||
- (!enable && nr_active_streams == 1))
+ (!enable && !keep_active))
fsl_ssi_rxtx_config(ssi_private, enable);
goto config_done;
@@ -551,9 +391,12 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
*/
/* These assignments are simply vals without bits set in avals*/
- sier = vals->sier & (vals->sier ^ avals->sier);
- srcr = vals->srcr & (vals->srcr ^ avals->srcr);
- stcr = vals->stcr & (vals->stcr ^ avals->stcr);
+ sier = fsl_ssi_disable_val(vals->sier, avals->sier,
+ keep_active);
+ srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr,
+ keep_active);
+ stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr,
+ keep_active);
write_ssi_mask(&ssi->srcr, srcr, 0);
write_ssi_mask(&ssi->stcr, stcr, 0);
@@ -593,7 +436,7 @@ static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private)
reg->tx.stcr = CCSR_SSI_STCR_TFEN0;
reg->tx.scr = 0;
- if (!ssi_private->imx_ac97) {
+ if (!fsl_ssi_is_ac97(ssi_private)) {
reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE;
reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN;
reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE;
@@ -642,96 +485,6 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
}
-static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
-{
- struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
- u8 wm;
- int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
-
- fsl_ssi_setup_reg_vals(ssi_private);
-
- if (ssi_private->imx_ac97)
- ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
- else
- ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
-
- /*
- * Section 16.5 of the MPC8610 reference manual says that the SSI needs
- * to be disabled before updating the registers we set here.
- */
- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
-
- /*
- * Program the SSI into I2S Slave Non-Network Synchronous mode. Also
- * enable the transmit and receive FIFO.
- *
- * FIXME: Little-endian samples require a different shift dir
- */
- write_ssi_mask(&ssi->scr,
- CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
- CCSR_SSI_SCR_TFR_CLK_DIS |
- ssi_private->i2s_mode |
- (synchronous ? CCSR_SSI_SCR_SYN : 0));
-
- write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFSI |
- CCSR_SSI_STCR_TEFS | CCSR_SSI_STCR_TSCKP, &ssi->stcr);
-
- write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFSI |
- CCSR_SSI_SRCR_REFS | CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
-
- /*
- * The DC and PM bits are only used if the SSI is the clock master.
- */
-
- /*
- * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
- * use FIFO 1. We program the transmit water to signal a DMA transfer
- * if there are only two (or fewer) elements left in the FIFO. Two
- * elements equals one frame (left channel, right channel). This value,
- * however, depends on the depth of the transmit buffer.
- *
- * We set the watermark on the same level as the DMA burstsize. For
- * fiq it is probably better to use the biggest possible watermark
- * size.
- */
- if (ssi_private->use_dma)
- wm = ssi_private->fifo_depth - 2;
- else
- wm = ssi_private->fifo_depth;
-
- write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
- CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm),
- &ssi->sfcsr);
-
- /*
- * For ac97 interrupts are enabled with the startup of the substream
- * because it is also running without an active substream. Normally SSI
- * is only enabled when there is a substream.
- */
- if (ssi_private->imx_ac97)
- fsl_ssi_setup_ac97(ssi_private);
-
- /*
- * Set a default slot number so that there is no need for those common
- * cases like I2S mode to call the extra set_tdm_slot() any more.
- */
- if (!ssi_private->imx_ac97) {
- write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(2));
- write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(2));
- }
-
- if (ssi_private->use_dual_fifo) {
- write_ssi_mask(&ssi->srcr, 0, CCSR_SSI_SRCR_RFEN1);
- write_ssi_mask(&ssi->stcr, 0, CCSR_SSI_STCR_TFEN1);
- write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TCH_EN);
- }
-
- return 0;
-}
-
-
/**
* fsl_ssi_startup: create a new substream
*
@@ -748,12 +501,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
snd_soc_dai_get_drvdata(rtd->cpu_dai);
unsigned long flags;
- /* First, we only do fsl_ssi_setup() when SSI is going to be active.
- * Second, fsl_ssi_setup was already called by ac97_init earlier if
- * the driver is in ac97 mode.
- */
- if (!dai->active && !ssi_private->imx_ac97) {
- fsl_ssi_setup(ssi_private);
+ if (!dai->active && !fsl_ssi_is_ac97(ssi_private)) {
spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
ssi_private->baudclk_locked = false;
spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
@@ -772,6 +520,102 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
}
/**
+ * fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock
+ *
+ * Note: This function can be only called when using SSI as DAI master
+ *
+ * Quick instruction for parameters:
+ * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels
+ * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK.
+ */
+static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret;
+ u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
+ unsigned long flags, clkrate, baudrate, tmprate;
+ u64 sub, savesub = 100000;
+
+ /* Don't apply it to any non-baudclk circumstance */
+ if (IS_ERR(ssi_private->baudclk))
+ return -EINVAL;
+
+ /* It should be already enough to divide clock by setting pm alone */
+ psr = 0;
+ div2 = 0;
+
+ factor = (div2 + 1) * (7 * psr + 1) * 2;
+
+ for (i = 0; i < 255; i++) {
+ /* The bclk rate must be smaller than 1/5 sysclk rate */
+ if (factor * (i + 1) < 5)
+ continue;
+
+ tmprate = freq * factor * (i + 2);
+ clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
+
+ do_div(clkrate, factor);
+ afreq = (u32)clkrate / (i + 1);
+
+ if (freq == afreq)
+ sub = 0;
+ else if (freq / afreq == 1)
+ sub = freq - afreq;
+ else if (afreq / freq == 1)
+ sub = afreq - freq;
+ else
+ continue;
+
+ /* Calculate the fraction */
+ sub *= 100000;
+ do_div(sub, freq);
+
+ if (sub < savesub) {
+ baudrate = tmprate;
+ savesub = sub;
+ pm = i;
+ }
+
+ /* We are lucky */
+ if (savesub == 0)
+ break;
+ }
+
+ /* No proper pm found if it is still remaining the initial value */
+ if (pm == 999) {
+ dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
+ return -EINVAL;
+ }
+
+ stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) |
+ (psr ? CCSR_SSI_SxCCR_PSR : 0);
+ mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 |
+ CCSR_SSI_SxCCR_PSR;
+
+ if (dir == SND_SOC_CLOCK_OUT || synchronous)
+ write_ssi_mask(&ssi->stccr, mask, stccr);
+ else
+ write_ssi_mask(&ssi->srccr, mask, stccr);
+
+ spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
+ if (!ssi_private->baudclk_locked) {
+ ret = clk_set_rate(ssi_private->baudclk, baudrate);
+ if (ret) {
+ spin_unlock_irqrestore(&ssi_private->baudclk_lock,
+ flags);
+ dev_err(cpu_dai->dev, "failed to set baudclk rate\n");
+ return -EINVAL;
+ }
+ ssi_private->baudclk_locked = true;
+ }
+ spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
+
+ return 0;
+}
+
+/**
* fsl_ssi_hw_params - program the sample size
*
* Most of the SSI registers have been programmed in the startup function,
@@ -819,7 +663,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
else
write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
- if (!ssi_private->imx_ac97)
+ if (!fsl_ssi_is_ac97(ssi_private))
write_ssi_mask(&ssi->scr,
CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
channels == 1 ? 0 : ssi_private->i2s_mode);
@@ -835,9 +679,14 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
u32 strcr = 0, stcr, srcr, scr, mask;
+ u8 wm;
+
+ ssi_private->dai_fmt = fmt;
+
+ fsl_ssi_setup_reg_vals(ssi_private);
scr = read_ssi(&ssi->scr) & ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK);
- scr |= CCSR_SSI_SCR_NET;
+ scr |= CCSR_SSI_SCR_SYNC_TX_FS;
mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR |
CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL |
@@ -845,19 +694,19 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
stcr = read_ssi(&ssi->stcr) & ~mask;
srcr = read_ssi(&ssi->srcr) & ~mask;
+ ssi_private->i2s_mode = CCSR_SSI_SCR_NET;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
- ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_MASTER;
+ ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER;
break;
case SND_SOC_DAIFMT_CBM_CFM:
- ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+ ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE;
break;
default:
return -EINVAL;
}
- scr |= ssi_private->i2s_mode;
/* Data on rising edge of bclk, frame low, 1clk before data */
strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP |
@@ -877,9 +726,13 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
CCSR_SSI_STCR_TXBIT0;
break;
+ case SND_SOC_DAIFMT_AC97:
+ ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL;
+ break;
default:
return -EINVAL;
}
+ scr |= ssi_private->i2s_mode;
/* DAI clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -929,99 +782,37 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
write_ssi(srcr, &ssi->srcr);
write_ssi(scr, &ssi->scr);
- return 0;
-}
-
-/**
- * fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock
- *
- * Note: This function can be only called when using SSI as DAI master
- *
- * Quick instruction for parameters:
- * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels
- * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK.
- */
-static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
- struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
- int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret;
- u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
- unsigned long flags, clkrate, baudrate, tmprate;
- u64 sub, savesub = 100000;
-
- /* Don't apply it to any non-baudclk circumstance */
- if (IS_ERR(ssi_private->baudclk))
- return -EINVAL;
-
- /* It should be already enough to divide clock by setting pm alone */
- psr = 0;
- div2 = 0;
-
- factor = (div2 + 1) * (7 * psr + 1) * 2;
-
- for (i = 0; i < 255; i++) {
- /* The bclk rate must be smaller than 1/5 sysclk rate */
- if (factor * (i + 1) < 5)
- continue;
-
- tmprate = freq * factor * (i + 2);
- clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
-
- do_div(clkrate, factor);
- afreq = (u32)clkrate / (i + 1);
-
- if (freq == afreq)
- sub = 0;
- else if (freq / afreq == 1)
- sub = freq - afreq;
- else if (afreq / freq == 1)
- sub = afreq - freq;
- else
- continue;
-
- /* Calculate the fraction */
- sub *= 100000;
- do_div(sub, freq);
-
- if (sub < savesub) {
- baudrate = tmprate;
- savesub = sub;
- pm = i;
- }
+ /*
+ * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
+ * use FIFO 1. We program the transmit water to signal a DMA transfer
+ * if there are only two (or fewer) elements left in the FIFO. Two
+ * elements equals one frame (left channel, right channel). This value,
+ * however, depends on the depth of the transmit buffer.
+ *
+ * We set the watermark on the same level as the DMA burstsize. For
+ * fiq it is probably better to use the biggest possible watermark
+ * size.
+ */
+ if (ssi_private->use_dma)
+ wm = ssi_private->fifo_depth - 2;
+ else
+ wm = ssi_private->fifo_depth;
- /* We are lucky */
- if (savesub == 0)
- break;
- }
+ write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
+ CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm),
+ &ssi->sfcsr);
- /* No proper pm found if it is still remaining the initial value */
- if (pm == 999) {
- dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
- return -EINVAL;
+ if (ssi_private->use_dual_fifo) {
+ write_ssi_mask(&ssi->srcr, CCSR_SSI_SRCR_RFEN1,
+ CCSR_SSI_SRCR_RFEN1);
+ write_ssi_mask(&ssi->stcr, CCSR_SSI_STCR_TFEN1,
+ CCSR_SSI_STCR_TFEN1);
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TCH_EN,
+ CCSR_SSI_SCR_TCH_EN);
}
- stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) |
- (psr ? CCSR_SSI_SxCCR_PSR : 0);
- mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 | CCSR_SSI_SxCCR_PSR;
-
- if (dir == SND_SOC_CLOCK_OUT || synchronous)
- write_ssi_mask(&ssi->stccr, mask, stccr);
- else
- write_ssi_mask(&ssi->srccr, mask, stccr);
-
- spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
- if (!ssi_private->baudclk_locked) {
- ret = clk_set_rate(ssi_private->baudclk, baudrate);
- if (ret) {
- spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
- dev_err(cpu_dai->dev, "failed to set baudclk rate\n");
- return -EINVAL;
- }
- ssi_private->baudclk_locked = true;
- }
- spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
+ if (fmt & SND_SOC_DAIFMT_AC97)
+ fsl_ssi_setup_ac97(ssi_private);
return 0;
}
@@ -1097,7 +888,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
else
fsl_ssi_rx_config(ssi_private, false);
- if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
+ if (!fsl_ssi_is_ac97(ssi_private) && (read_ssi(&ssi->scr) &
(CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) {
spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
ssi_private->baudclk_locked = false;
@@ -1109,7 +900,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
return -EINVAL;
}
- if (ssi_private->imx_ac97) {
+ if (fsl_ssi_is_ac97(ssi_private)) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
else
@@ -1123,7 +914,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
{
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
- if (ssi_private->ssi_on_imx && ssi_private->use_dma) {
+ if (fsl_ssi_on_imx(ssi_private) && ssi_private->use_dma) {
dai->playback_dma_data = &ssi_private->dma_params_tx;
dai->capture_dma_data = &ssi_private->dma_params_rx;
}
@@ -1184,11 +975,6 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
static struct fsl_ssi_private *fsl_ac97_data;
-static void fsl_ssi_ac97_init(void)
-{
- fsl_ssi_setup(fsl_ac97_data);
-}
-
static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
@@ -1251,11 +1037,107 @@ static void make_lowercase(char *s)
}
}
+static int fsl_ssi_imx_probe(struct platform_device *pdev,
+ struct fsl_ssi_private *ssi_private, void __iomem *iomem)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 dmas[4];
+ int ret;
+
+ ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(ssi_private->clk)) {
+ ret = PTR_ERR(ssi_private->clk);
+ dev_err(&pdev->dev, "could not get clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ssi_private->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ /* For those SLAVE implementations, we ingore non-baudclk cases
+ * and, instead, abandon MASTER mode that needs baud clock.
+ */
+ ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
+ if (IS_ERR(ssi_private->baudclk))
+ dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
+ PTR_ERR(ssi_private->baudclk));
+ else
+ clk_prepare_enable(ssi_private->baudclk);
+
+ /*
+ * We have burstsize be "fifo_depth - 2" to match the SSI
+ * watermark setting in fsl_ssi_startup().
+ */
+ ssi_private->dma_params_tx.maxburst = ssi_private->fifo_depth - 2;
+ ssi_private->dma_params_rx.maxburst = ssi_private->fifo_depth - 2;
+ ssi_private->dma_params_tx.addr = ssi_private->ssi_phys +
+ offsetof(struct ccsr_ssi, stx0);
+ ssi_private->dma_params_rx.addr = ssi_private->ssi_phys +
+ offsetof(struct ccsr_ssi, srx0);
+
+ ret = !of_property_read_u32_array(np, "dmas", dmas, 4);
+ if (ssi_private->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
+ ssi_private->use_dual_fifo = true;
+ /* When using dual fifo mode, we need to keep watermark
+ * as even numbers due to dma script limitation.
+ */
+ ssi_private->dma_params_tx.maxburst &= ~0x1;
+ ssi_private->dma_params_rx.maxburst &= ~0x1;
+ }
+
+ if (!ssi_private->use_dma) {
+
+ /*
+ * Some boards use an incompatible codec. To get it
+ * working, we are using imx-fiq-pcm-audio, that
+ * can handle those codecs. DMA is not possible in this
+ * situation.
+ */
+
+ ssi_private->fiq_params.irq = ssi_private->irq;
+ ssi_private->fiq_params.base = iomem;
+ ssi_private->fiq_params.dma_params_rx =
+ &ssi_private->dma_params_rx;
+ ssi_private->fiq_params.dma_params_tx =
+ &ssi_private->dma_params_tx;
+
+ ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
+ if (ret)
+ goto error_pcm;
+ } else {
+ ret = imx_pcm_dma_init(pdev);
+ if (ret)
+ goto error_pcm;
+ }
+
+ return 0;
+
+error_pcm:
+ if (!IS_ERR(ssi_private->baudclk))
+ clk_disable_unprepare(ssi_private->baudclk);
+
+ clk_disable_unprepare(ssi_private->clk);
+
+ return ret;
+}
+
+static void fsl_ssi_imx_clean(struct platform_device *pdev,
+ struct fsl_ssi_private *ssi_private)
+{
+ if (!ssi_private->use_dma)
+ imx_pcm_fiq_exit(pdev);
+ if (!IS_ERR(ssi_private->baudclk))
+ clk_disable_unprepare(ssi_private->baudclk);
+ clk_disable_unprepare(ssi_private->clk);
+}
+
static int fsl_ssi_probe(struct platform_device *pdev)
{
struct fsl_ssi_private *ssi_private;
int ret = 0;
- struct device_attribute *dev_attr = NULL;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id;
enum fsl_ssi_type hw_type;
@@ -1263,7 +1145,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
const uint32_t *iprop;
struct resource res;
char name[64];
- bool shared;
bool ac97 = false;
/* SSIs that are not connected on the board should have a
@@ -1286,17 +1167,13 @@ static int fsl_ssi_probe(struct platform_device *pdev)
if (!strcmp(sprop, "ac97-slave"))
ac97 = true;
- /* The DAI name is the last part of the full name of the node. */
- p = strrchr(np->full_name, '/') + 1;
- ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p),
- GFP_KERNEL);
+ ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private),
+ GFP_KERNEL);
if (!ssi_private) {
dev_err(&pdev->dev, "could not allocate DAI object\n");
return -ENOMEM;
}
- strcpy(ssi_private->name, p);
-
ssi_private->use_dma = !of_property_read_bool(np,
"fsl,fiq-stream-filter");
ssi_private->hw_type = hw_type;
@@ -1306,7 +1183,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
sizeof(fsl_ssi_ac97_dai));
fsl_ac97_data = ssi_private;
- ssi_private->imx_ac97 = true;
snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
} else {
@@ -1314,7 +1190,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
sizeof(fsl_ssi_dai_template));
}
- ssi_private->cpu_dai_drv.name = ssi_private->name;
+ ssi_private->cpu_dai_drv.name = dev_name(&pdev->dev);
/* Get the addresses and IRQ */
ret = of_address_to_resource(np, 0, &res);
@@ -1353,177 +1229,43 @@ static int fsl_ssi_probe(struct platform_device *pdev)
ssi_private->baudclk_locked = false;
spin_lock_init(&ssi_private->baudclk_lock);
- /*
- * imx51 and later SoCs have a slightly different IP that allows the
- * SSI configuration while the SSI unit is running.
- *
- * More important, it is necessary on those SoCs to configure the
- * sperate TX/RX DMA bits just before starting the stream
- * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi
- * sends any DMA requests to the SDMA unit, otherwise it is not defined
- * how the SDMA unit handles the DMA request.
- *
- * SDMA units are present on devices starting at imx35 but the imx35
- * reference manual states that the DMA bits should not be changed
- * while the SSI unit is running (SSIEN). So we support the necessary
- * online configuration of fsl-ssi starting at imx51.
- */
- switch (hw_type) {
- case FSL_SSI_MCP8610:
- case FSL_SSI_MX21:
- case FSL_SSI_MX35:
- ssi_private->offline_config = true;
- break;
- case FSL_SSI_MX51:
- ssi_private->offline_config = false;
- break;
- }
-
- if (hw_type == FSL_SSI_MX21 || hw_type == FSL_SSI_MX51 ||
- hw_type == FSL_SSI_MX35) {
- u32 dma_events[2], dmas[4];
- ssi_private->ssi_on_imx = true;
+ dev_set_drvdata(&pdev->dev, ssi_private);
- ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(ssi_private->clk)) {
- ret = PTR_ERR(ssi_private->clk);
- dev_err(&pdev->dev, "could not get clock: %d\n", ret);
- goto error_irqmap;
- }
- ret = clk_prepare_enable(ssi_private->clk);
- if (ret) {
- dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n",
- ret);
+ if (fsl_ssi_on_imx(ssi_private)) {
+ ret = fsl_ssi_imx_probe(pdev, ssi_private, ssi_private->ssi);
+ if (ret)
goto error_irqmap;
- }
-
- /* For those SLAVE implementations, we ingore non-baudclk cases
- * and, instead, abandon MASTER mode that needs baud clock.
- */
- ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
- if (IS_ERR(ssi_private->baudclk))
- dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
- PTR_ERR(ssi_private->baudclk));
- else
- clk_prepare_enable(ssi_private->baudclk);
-
- /*
- * We have burstsize be "fifo_depth - 2" to match the SSI
- * watermark setting in fsl_ssi_startup().
- */
- ssi_private->dma_params_tx.maxburst =
- ssi_private->fifo_depth - 2;
- ssi_private->dma_params_rx.maxburst =
- ssi_private->fifo_depth - 2;
- ssi_private->dma_params_tx.addr =
- ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
- ssi_private->dma_params_rx.addr =
- ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
- ssi_private->dma_params_tx.filter_data =
- &ssi_private->filter_data_tx;
- ssi_private->dma_params_rx.filter_data =
- &ssi_private->filter_data_rx;
- if (!of_property_read_bool(pdev->dev.of_node, "dmas") &&
- ssi_private->use_dma) {
- /*
- * FIXME: This is a temporary solution until all
- * necessary dma drivers support the generic dma
- * bindings.
- */
- ret = of_property_read_u32_array(pdev->dev.of_node,
- "fsl,ssi-dma-events", dma_events, 2);
- if (ret && ssi_private->use_dma) {
- dev_err(&pdev->dev, "could not get dma events but fsl-ssi is configured to use DMA\n");
- goto error_clk;
- }
- }
- /* Should this be merge with the above? */
- if (!of_property_read_u32_array(pdev->dev.of_node, "dmas", dmas, 4)
- && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
- ssi_private->use_dual_fifo = true;
- /* When using dual fifo mode, we need to keep watermark
- * as even numbers due to dma script limitation.
- */
- ssi_private->dma_params_tx.maxburst &= ~0x1;
- ssi_private->dma_params_rx.maxburst &= ~0x1;
- }
-
- shared = of_device_is_compatible(of_get_parent(np),
- "fsl,spba-bus");
+ }
- imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx,
- dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
- imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
- dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
+ ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
+ &ssi_private->cpu_dai_drv, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+ goto error_asoc_register;
}
- /*
- * Enable interrupts only for MCP8610 and MX51. The other MXs have
- * different writeable interrupt status registers.
- */
if (ssi_private->use_dma) {
- /* The 'name' should not have any slashes in it. */
ret = devm_request_irq(&pdev->dev, ssi_private->irq,
- fsl_ssi_isr, 0, ssi_private->name,
+ fsl_ssi_isr, 0, dev_name(&pdev->dev),
ssi_private);
- ssi_private->irq_stats = true;
if (ret < 0) {
dev_err(&pdev->dev, "could not claim irq %u\n",
ssi_private->irq);
- goto error_clk;
+ goto error_irq;
}
}
- /* Register with ASoC */
- dev_set_drvdata(&pdev->dev, ssi_private);
-
- ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
- &ssi_private->cpu_dai_drv, 1);
- if (ret) {
- dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
- goto error_dev;
- }
-
- ret = fsl_ssi_debugfs_create(ssi_private, &pdev->dev);
+ ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
if (ret)
- goto error_dbgfs;
-
- if (ssi_private->ssi_on_imx) {
- if (!ssi_private->use_dma) {
-
- /*
- * Some boards use an incompatible codec. To get it
- * working, we are using imx-fiq-pcm-audio, that
- * can handle those codecs. DMA is not possible in this
- * situation.
- */
-
- ssi_private->fiq_params.irq = ssi_private->irq;
- ssi_private->fiq_params.base = ssi_private->ssi;
- ssi_private->fiq_params.dma_params_rx =
- &ssi_private->dma_params_rx;
- ssi_private->fiq_params.dma_params_tx =
- &ssi_private->dma_params_tx;
-
- ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
- if (ret)
- goto error_pcm;
- } else {
- ret = imx_pcm_dma_init(pdev);
- if (ret)
- goto error_pcm;
- }
- }
+ goto error_asoc_register;
/*
* If codec-handle property is missing from SSI node, we assume
* that the machine driver uses new binding which does not require
* SSI driver to trigger machine driver's probe.
*/
- if (!of_get_property(np, "codec-handle", NULL)) {
- ssi_private->new_binding = true;
+ if (!of_get_property(np, "codec-handle", NULL))
goto done;
- }
/* Trigger the machine driver's probe function. The platform driver
* name of the machine driver is taken from /compatible property of the
@@ -1543,37 +1285,24 @@ static int fsl_ssi_probe(struct platform_device *pdev)
if (IS_ERR(ssi_private->pdev)) {
ret = PTR_ERR(ssi_private->pdev);
dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
- goto error_dai;
+ goto error_sound_card;
}
done:
- if (ssi_private->imx_ac97)
- fsl_ssi_ac97_init();
-
return 0;
-error_dai:
- if (ssi_private->ssi_on_imx && !ssi_private->use_dma)
- imx_pcm_fiq_exit(pdev);
-
-error_pcm:
- fsl_ssi_debugfs_remove(ssi_private);
+error_sound_card:
+ fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
-error_dbgfs:
+error_irq:
snd_soc_unregister_component(&pdev->dev);
-error_dev:
- device_remove_file(&pdev->dev, dev_attr);
-
-error_clk:
- if (ssi_private->ssi_on_imx) {
- if (!IS_ERR(ssi_private->baudclk))
- clk_disable_unprepare(ssi_private->baudclk);
- clk_disable_unprepare(ssi_private->clk);
- }
+error_asoc_register:
+ if (fsl_ssi_on_imx(ssi_private))
+ fsl_ssi_imx_clean(pdev, ssi_private);
error_irqmap:
- if (ssi_private->irq_stats)
+ if (ssi_private->use_dma)
irq_dispose_mapping(ssi_private->irq);
return ret;
@@ -1583,17 +1312,16 @@ static int fsl_ssi_remove(struct platform_device *pdev)
{
struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
- fsl_ssi_debugfs_remove(ssi_private);
+ fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
- if (!ssi_private->new_binding)
+ if (ssi_private->pdev)
platform_device_unregister(ssi_private->pdev);
snd_soc_unregister_component(&pdev->dev);
- if (ssi_private->ssi_on_imx) {
- if (!IS_ERR(ssi_private->baudclk))
- clk_disable_unprepare(ssi_private->baudclk);
- clk_disable_unprepare(ssi_private->clk);
- }
- if (ssi_private->irq_stats)
+
+ if (fsl_ssi_on_imx(ssi_private))
+ fsl_ssi_imx_clean(pdev, ssi_private);
+
+ if (ssi_private->use_dma)
irq_dispose_mapping(ssi_private->irq);
return 0;
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
index e6b6324..71c3e7e 100644
--- a/sound/soc/fsl/fsl_ssi.h
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -39,6 +39,7 @@ struct ccsr_ssi {
__be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */
};
+#define CCSR_SSI_SCR_SYNC_TX_FS 0x00001000
#define CCSR_SSI_SCR_RFR_CLK_DIS 0x00000800
#define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400
#define CCSR_SSI_SCR_TCH_EN 0x00000100
@@ -206,5 +207,64 @@ struct ccsr_ssi {
#define CCSR_SSI_SACNT_FV 0x00000002
#define CCSR_SSI_SACNT_AC97EN 0x00000001
-#endif
+struct device;
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+
+struct fsl_ssi_dbg {
+ struct dentry *dbg_dir;
+ struct dentry *dbg_stats;
+
+ struct {
+ unsigned int rfrc;
+ unsigned int tfrc;
+ unsigned int cmdau;
+ unsigned int cmddu;
+ unsigned int rxt;
+ unsigned int rdr1;
+ unsigned int rdr0;
+ unsigned int tde1;
+ unsigned int tde0;
+ unsigned int roe1;
+ unsigned int roe0;
+ unsigned int tue1;
+ unsigned int tue0;
+ unsigned int tfs;
+ unsigned int rfs;
+ unsigned int tls;
+ unsigned int rls;
+ unsigned int rff1;
+ unsigned int rff0;
+ unsigned int tfe1;
+ unsigned int tfe0;
+ } stats;
+};
+
+void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *ssi_dbg, u32 sisr);
+
+int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev);
+
+void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg);
+
+#else
+
+struct fsl_ssi_dbg {
+};
+
+static inline void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *stats, u32 sisr)
+{
+}
+
+static inline int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg,
+ struct device *dev)
+{
+ return 0;
+}
+
+static inline void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
+{
+}
+#endif /* ! IS_ENABLED(CONFIG_DEBUG_FS) */
+
+#endif
diff --git a/sound/soc/fsl/fsl_ssi_dbg.c b/sound/soc/fsl/fsl_ssi_dbg.c
new file mode 100644
index 0000000..5469ffb
--- /dev/null
+++ b/sound/soc/fsl/fsl_ssi_dbg.c
@@ -0,0 +1,163 @@
+/*
+ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) debugging functions
+ *
+ * Copyright 2014 Markus Pargmann <mpa@pengutronix.de>, Pengutronix
+ *
+ * Splitted from fsl_ssi.c
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include "fsl_ssi.h"
+
+void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *dbg, u32 sisr)
+{
+ if (sisr & CCSR_SSI_SISR_RFRC)
+ dbg->stats.rfrc++;
+
+ if (sisr & CCSR_SSI_SISR_TFRC)
+ dbg->stats.tfrc++;
+
+ if (sisr & CCSR_SSI_SISR_CMDAU)
+ dbg->stats.cmdau++;
+
+ if (sisr & CCSR_SSI_SISR_CMDDU)
+ dbg->stats.cmddu++;
+
+ if (sisr & CCSR_SSI_SISR_RXT)
+ dbg->stats.rxt++;
+
+ if (sisr & CCSR_SSI_SISR_RDR1)
+ dbg->stats.rdr1++;
+
+ if (sisr & CCSR_SSI_SISR_RDR0)
+ dbg->stats.rdr0++;
+
+ if (sisr & CCSR_SSI_SISR_TDE1)
+ dbg->stats.tde1++;
+
+ if (sisr & CCSR_SSI_SISR_TDE0)
+ dbg->stats.tde0++;
+
+ if (sisr & CCSR_SSI_SISR_ROE1)
+ dbg->stats.roe1++;
+
+ if (sisr & CCSR_SSI_SISR_ROE0)
+ dbg->stats.roe0++;
+
+ if (sisr & CCSR_SSI_SISR_TUE1)
+ dbg->stats.tue1++;
+
+ if (sisr & CCSR_SSI_SISR_TUE0)
+ dbg->stats.tue0++;
+
+ if (sisr & CCSR_SSI_SISR_TFS)
+ dbg->stats.tfs++;
+
+ if (sisr & CCSR_SSI_SISR_RFS)
+ dbg->stats.rfs++;
+
+ if (sisr & CCSR_SSI_SISR_TLS)
+ dbg->stats.tls++;
+
+ if (sisr & CCSR_SSI_SISR_RLS)
+ dbg->stats.rls++;
+
+ if (sisr & CCSR_SSI_SISR_RFF1)
+ dbg->stats.rff1++;
+
+ if (sisr & CCSR_SSI_SISR_RFF0)
+ dbg->stats.rff0++;
+
+ if (sisr & CCSR_SSI_SISR_TFE1)
+ dbg->stats.tfe1++;
+
+ if (sisr & CCSR_SSI_SISR_TFE0)
+ dbg->stats.tfe0++;
+}
+
+/* Show the statistics of a flag only if its interrupt is enabled. The
+ * compiler will optimze this code to a no-op if the interrupt is not
+ * enabled.
+ */
+#define SIER_SHOW(flag, name) \
+ do { \
+ if (CCSR_SSI_SIER_##flag) \
+ seq_printf(s, #name "=%u\n", ssi_dbg->stats.name); \
+ } while (0)
+
+
+/**
+ * fsl_sysfs_ssi_show: display SSI statistics
+ *
+ * Display the statistics for the current SSI device. To avoid confusion,
+ * we only show those counts that are enabled.
+ */
+static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
+{
+ struct fsl_ssi_dbg *ssi_dbg = s->private;
+
+ SIER_SHOW(RFRC_EN, rfrc);
+ SIER_SHOW(TFRC_EN, tfrc);
+ SIER_SHOW(CMDAU_EN, cmdau);
+ SIER_SHOW(CMDDU_EN, cmddu);
+ SIER_SHOW(RXT_EN, rxt);
+ SIER_SHOW(RDR1_EN, rdr1);
+ SIER_SHOW(RDR0_EN, rdr0);
+ SIER_SHOW(TDE1_EN, tde1);
+ SIER_SHOW(TDE0_EN, tde0);
+ SIER_SHOW(ROE1_EN, roe1);
+ SIER_SHOW(ROE0_EN, roe0);
+ SIER_SHOW(TUE1_EN, tue1);
+ SIER_SHOW(TUE0_EN, tue0);
+ SIER_SHOW(TFS_EN, tfs);
+ SIER_SHOW(RFS_EN, rfs);
+ SIER_SHOW(TLS_EN, tls);
+ SIER_SHOW(RLS_EN, rls);
+ SIER_SHOW(RFF1_EN, rff1);
+ SIER_SHOW(RFF0_EN, rff0);
+ SIER_SHOW(TFE1_EN, tfe1);
+ SIER_SHOW(TFE0_EN, tfe0);
+
+ return 0;
+}
+
+static int fsl_ssi_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fsl_ssi_stats_show, inode->i_private);
+}
+
+static const struct file_operations fsl_ssi_stats_ops = {
+ .open = fsl_ssi_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev)
+{
+ ssi_dbg->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
+ if (!ssi_dbg->dbg_dir)
+ return -ENOMEM;
+
+ ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO,
+ ssi_dbg->dbg_dir, ssi_dbg, &fsl_ssi_stats_ops);
+ if (!ssi_dbg->dbg_stats) {
+ debugfs_remove(ssi_dbg->dbg_dir);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
+{
+ debugfs_remove(ssi_dbg->dbg_stats);
+ debugfs_remove(ssi_dbg->dbg_dir);
+}
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index 2585ae4..0849b7b 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -40,7 +40,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = 65535, /* Limited by SDMA engine */
diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c
index e94aa02..057e5ef 100644
--- a/sound/soc/soc-devres.c
+++ b/sound/soc/soc-devres.c
@@ -85,6 +85,7 @@ int devm_snd_soc_register_platform(struct device *dev,
return ret;
}
+EXPORT_SYMBOL_GPL(devm_snd_soc_register_platform);
static void devm_card_release(struct device *dev, void *res)
{
OpenPOWER on IntegriCloud