static int imx_ssi_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; void __iomem *ioaddr = priv->ioaddr; /* we cant really change any SSI values after SSI is enabled * need to fix in software for max flexibility - lrg */ if (cpu_dai->playback.active || cpu_dai->capture.active) return 0; /* reset the SSI port - Sect 45.4.4 */ if (clk_get_usecount(priv->ssi_clk) != 0) { clk_enable(priv->ssi_clk); return 0; } __raw_writel(0, ioaddr + SSI_SCR); clk_enable(priv->ssi_clk); /* BIG FAT WARNING * SDMA FIFO watermark must == SSI FIFO watermark for best results. */ __raw_writel((SSI_SFCSR_RFWM1(SSI_RXFIFO_WATERMARK) | SSI_SFCSR_RFWM0(SSI_RXFIFO_WATERMARK) | SSI_SFCSR_TFWM1(SSI_TXFIFO_WATERMARK) | SSI_SFCSR_TFWM0(SSI_TXFIFO_WATERMARK)), ioaddr + SSI_SFCSR); __raw_writel(0, ioaddr + SSI_SIER); SSI_DUMP(); return 0; }
static int imx_ssi_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; /* we cant really change any SSI values after SSI is enabled * need to fix in software for max flexibility - lrg */ if (cpu_dai->playback.active || cpu_dai->capture.active) return 0; /* reset the SSI port - Sect 45.4.4 */ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) { if (ssi_active[SSI1_PORT]++) return 0; __raw_writel(0, SSI1_SCR); ssi1_clk = clk_get(NULL, "ssi_clk.0"); clk_enable(ssi1_clk); /* BIG FAT WARNING * SDMA FIFO watermark must == SSI FIFO watermark for * best results. */ __raw_writel((SSI_SFCSR_RFWM1(SSI_RXFIFO_WATERMARK) | SSI_SFCSR_RFWM0(SSI_RXFIFO_WATERMARK) | SSI_SFCSR_TFWM1(SSI_TXFIFO_WATERMARK) | SSI_SFCSR_TFWM0(SSI_TXFIFO_WATERMARK)), SSI1_SFCSR); __raw_writel(0, SSI1_SIER); } else { if (ssi_active[SSI2_PORT]++) return 0; __raw_writel(0, SSI2_SCR); ssi2_clk = clk_get(NULL, "ssi_clk.1"); clk_enable(ssi2_clk); /* above warning applies here too */ __raw_writel((SSI_SFCSR_RFWM1(SSI_RXFIFO_WATERMARK) | SSI_SFCSR_RFWM0(SSI_RXFIFO_WATERMARK) | SSI_SFCSR_TFWM1(SSI_TXFIFO_WATERMARK) | SSI_SFCSR_TFWM0(SSI_TXFIFO_WATERMARK)), SSI2_SFCSR); __raw_writel(0, SSI2_SIER); } SSI_DUMP(); return 0; }
static int imx_ssi_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; void __iomem *ioaddr = priv->ioaddr; u32 scr; /* enable the SSI port, note that no other port config * should happen after SSIEN is set */ scr = __raw_readl(ioaddr + SSI_SCR); __raw_writel((scr | SSI_SCR_SSIEN), ioaddr + SSI_SCR); SSI_DUMP(); return 0; }
static int imx_ssi_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_link *pcm_link = substream->private_data; struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; u32 scr; /* enable the SSI port, note that no other port config * should happen after SSIEN is set */ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) { scr = SSI1_SCR; SSI1_SCR = scr | SSI_SCR_SSIEN; } else { scr = SSI2_SCR; SSI2_SCR = scr | SSI_SCR_SSIEN; } SSI_DUMP(); return 0; }
static int imx_ssi_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; u32 scr; /* enable the SSI port, note that no other port config * should happen after SSIEN is set */ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) { scr = __raw_readl(SSI1_SCR); __raw_writel((scr | SSI_SCR_SSIEN), SSI1_SCR); } else { scr = __raw_readl(SSI2_SCR); __raw_writel((scr | SSI_SCR_SSIEN), SSI2_SCR); } SSI_DUMP(); return 0; }
static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_link *pcm_link = substream->private_data; struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; u32 scr; if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) scr = SSI1_SCR; else scr = SSI2_SCR; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) scr |= SSI_SCR_TE; else scr |= SSI_SCR_RE; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) scr &= ~SSI_SCR_TE; else scr &= ~SSI_SCR_RE; break; default: return -EINVAL; } if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) SSI1_SCR = scr; else SSI2_SCR = scr; SSI_DUMP(); return 0; }
static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; void __iomem *ioaddr = priv->ioaddr; u32 scr; scr = __raw_readl(ioaddr + SSI_SCR); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (scr & SSI_SCR_RE) { __raw_writel(0, ioaddr + SSI_SCR); } scr |= SSI_SCR_TE; } else scr |= SSI_SCR_RE; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) scr &= ~SSI_SCR_TE; else scr &= ~SSI_SCR_RE; break; default: return -EINVAL; } __raw_writel(scr, ioaddr + SSI_SCR); SSI_DUMP(); return 0; }
/* * SSI DAI format configuration. * Should only be called when port is inactive (i.e. SSIEN = 0). * Note: We don't use the I2S modes but instead manually configure the * SSI for I2S. */ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; void __iomem *ioaddr = priv->ioaddr; u32 stcr = 0, srcr = 0, scr; scr = __raw_readl(ioaddr + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET); if (scr & SSI_SCR_SSIEN) return 0; /* DAI mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* data on rising edge of bclk, frame low 1clk before data */ stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0; break; case SND_SOC_DAIFMT_LEFT_J: /* data on rising edge of bclk, frame high with data */ stcr |= SSI_STCR_TXBIT0; srcr |= SSI_SRCR_RXBIT0; break; case SND_SOC_DAIFMT_DSP_B: /* data on rising edge of bclk, frame high with data */ stcr |= SSI_STCR_TFSL; srcr |= SSI_SRCR_RFSL; break; case SND_SOC_DAIFMT_DSP_A: /* data on rising edge of bclk, frame high 1clk before data */ stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS; srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS; break; } /* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_IF: stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI); break; case SND_SOC_DAIFMT_IB_NF: stcr |= SSI_STCR_TFSI; stcr &= ~SSI_STCR_TSCKP; srcr |= SSI_SRCR_RFSI; srcr &= ~SSI_SRCR_RSCKP; break; case SND_SOC_DAIFMT_NB_IF: stcr &= ~SSI_STCR_TFSI; stcr |= SSI_STCR_TSCKP; srcr &= ~SSI_SRCR_RFSI; srcr |= SSI_SRCR_RSCKP; break; case SND_SOC_DAIFMT_NB_NF: stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP; break; } /* DAI clock master masks */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR; if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) && priv->network_mode) { scr &= ~SSI_SCR_I2S_MODE_MASK; scr |= SSI_SCR_I2S_MODE_MSTR; } break; case SND_SOC_DAIFMT_CBM_CFS: stcr |= SSI_STCR_TFDIR; srcr |= SSI_SRCR_RFDIR; break; case SND_SOC_DAIFMT_CBS_CFM: stcr |= SSI_STCR_TXDIR; srcr |= SSI_SRCR_RXDIR; break; case SND_SOC_DAIFMT_CBM_CFM: if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) && priv->network_mode) { scr &= ~SSI_SCR_I2S_MODE_MASK; scr |= SSI_SCR_I2S_MODE_SLAVE; } break; } /* sync */ if (priv->sync_mode) scr |= SSI_SCR_SYN; /* tdm - only for stereo atm */ if (priv->network_mode) scr |= SSI_SCR_NET; #ifdef CONFIG_MXC_SSI_DUAL_FIFO if (cpu_is_mx51() || cpu_is_mx53()) { stcr |= SSI_STCR_TFEN1; srcr |= SSI_SRCR_RFEN1; scr |= SSI_SCR_TCH_EN; } #endif __raw_writel(stcr, ioaddr + SSI_STCR); __raw_writel(srcr, ioaddr + SSI_SRCR); __raw_writel(scr, ioaddr + SSI_SCR); SSI_DUMP(); return 0; }
/* * SSI DAI format configuration. * Should only be called when port is inactive (i.e. SSIEN = 0). * Note: We don't use the I2S modes but instead manually configure the * SSI for I2S. */ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { u32 stcr = 0, srcr = 0, scr; if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) scr = SSI1_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET); else scr = SSI2_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET); if (scr & SSI_SCR_SSIEN) return 0; /* DAI mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* data on rising edge of bclk, frame low 1clk before data */ stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0; break; case SND_SOC_DAIFMT_LEFT_J: /* data on rising edge of bclk, frame high with data */ stcr |= SSI_STCR_TXBIT0; srcr |= SSI_SRCR_RXBIT0; break; case SND_SOC_DAIFMT_DSP_B: /* data on rising edge of bclk, frame high with data */ stcr |= SSI_STCR_TFSL; srcr |= SSI_SRCR_RFSL; break; case SND_SOC_DAIFMT_DSP_A: /* data on rising edge of bclk, frame high 1clk before data */ stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS; srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS; break; } /* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_IF: stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI); break; case SND_SOC_DAIFMT_IB_NF: stcr |= SSI_STCR_TFSI; stcr &= ~SSI_STCR_TSCKP; srcr |= SSI_SRCR_RFSI; srcr &= ~SSI_SRCR_RSCKP; break; case SND_SOC_DAIFMT_NB_IF: stcr &= ~SSI_STCR_TFSI; stcr |= SSI_STCR_TSCKP; srcr &= ~SSI_SRCR_RFSI; srcr |= SSI_SRCR_RSCKP; break; case SND_SOC_DAIFMT_NB_NF: stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP; break; } /* DAI clock master masks */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR; srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR; if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) && (fmt & SND_SOC_DAIFMT_TDM)) { scr &= ~SSI_SCR_I2S_MODE_MASK; scr |= SSI_SCR_I2S_MODE_MSTR; } break; case SND_SOC_DAIFMT_CBM_CFS: stcr |= SSI_STCR_TFDIR; srcr |= SSI_SRCR_RFDIR; break; case SND_SOC_DAIFMT_CBS_CFM: stcr |= SSI_STCR_TXDIR; srcr |= SSI_SRCR_RXDIR; break; case SND_SOC_DAIFMT_CBM_CFM: if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) && (fmt & SND_SOC_DAIFMT_TDM)) { scr &= ~SSI_SCR_I2S_MODE_MASK; scr |= SSI_SCR_I2S_MODE_SLAVE; } break; } /* sync */ if (!(fmt & SND_SOC_DAIFMT_ASYNC)) scr |= SSI_SCR_SYN; /* tdm - only for stereo atm */ if (fmt & SND_SOC_DAIFMT_TDM) scr |= SSI_SCR_NET; if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) { SSI1_STCR = stcr; SSI1_SRCR = srcr; SSI1_SCR = scr; } else { SSI2_STCR = stcr; SSI2_SRCR = srcr; SSI2_SCR = scr; } SSI_DUMP(); return 0; }