Example #1
0
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;
}
Example #3
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;
}
Example #4
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;
}
Example #6
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;
}
Example #7
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;
}
Example #8
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;
}
Example #9
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;
}