Example #1
0
/*
 * Set the SSPA audio DMA parameters and sample size.
 * Can be called multiple times by oss emulation.
 */
static int mmp_sspa_hw_params(struct snd_pcm_substream *substream,
			       struct snd_pcm_hw_params *params,
			       struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai);
	struct ssp_device *sspa = sspa_priv->sspa;
	struct snd_dmaengine_dai_dma_data *dma_params;
	u32 sspa_ctrl;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_TXCTL);
	else
		sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_RXCTL);

	sspa_ctrl &= ~SSPA_CTL_XFRLEN1_MASK;
	sspa_ctrl |= SSPA_CTL_XFRLEN1(params_channels(params) - 1);
	sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;
	sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_32_BITS);
	sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK;

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S8:
		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_8_BITS);
		break;
	case SNDRV_PCM_FORMAT_S16_LE:
		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_16_BITS);
		break;
	case SNDRV_PCM_FORMAT_S20_3LE:
		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_20_BITS);
		break;
	case SNDRV_PCM_FORMAT_S24_3LE:
		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_24_BITS);
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_32_BITS);
		break;
	default:
		return -EINVAL;
	}

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl);
		mmp_sspa_write_reg(sspa, SSPA_TXFIFO_LL, 0x1);
	} else {
		mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);
		mmp_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0);
	}

	dma_params = &sspa_priv->dma_params[substream->stream];
	dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
				(sspa->phys_base + SSPA_TXD) :
				(sspa->phys_base + SSPA_RXD);
	snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params);
	return 0;
}
/*
 * Set up the sspa dai format. The sspa port must be inactive
 * before calling this function as the physical
 * interface format is changed.
 */
static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai,
				 unsigned int fmt)
{
	struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(cpu_dai);
	struct ssp_device *sspa = sspa_priv->sspa;
	u32 sspa_sp, sspa_ctrl, sp_reg, ctrl_reg;

	if (cpu_dai->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		sp_reg = SSPA_TXSP;
		ctrl_reg = SSPA_TXCTL;
	} else if (cpu_dai->stream == SNDRV_PCM_STREAM_CAPTURE) {
		sp_reg = SSPA_RXSP;
		ctrl_reg = SSPA_RXCTL;
	} else {
		/* don't configure anything */
		return 0;
	}
	/* reset port settings */
	sspa_sp = SSPA_SP_WEN | SSPA_SP_FIX;
	sspa_ctrl = 0;

	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		sspa_sp |= SSPA_SP_MSL;
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		break;
	default:
		return -EINVAL;
	}

	sspa_sp &= ~SSPA_SP_FSP;
	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		sspa_sp |= SSPA_SP_FSP;
		break;
	case SND_SOC_DAIFMT_NB_IF:
		break;
	default:
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		sspa_sp |= SSPA_TXSP_FPER(63);
		sspa_sp |= SSPA_SP_FWID(31);
		sspa_ctrl |= SSPA_CTL_XDATDLY(1);
		sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;
		sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_32_BITS);
		/* fix to sample shift issue in I2S mode */
		if (cpu_dai->stream == SNDRV_PCM_STREAM_PLAYBACK)
			sspa_sp |= SSPA_SP_FSP;
		break;
	case SND_SOC_DAIFMT_DSP_A:
		sspa_sp |= SSPA_TXSP_FPER(31);
		sspa_sp |= SSPA_SP_FWID(0);
		sspa_ctrl |= SSPA_CTL_XDATDLY(1);
		sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;
		sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_16_BITS);
		break;
	case SND_SOC_DAIFMT_DSP_B:
		sspa_sp |= SSPA_TXSP_FPER(31);
		sspa_sp |= SSPA_SP_FWID(0);
		sspa_ctrl |= SSPA_CTL_XDATDLY(0);
		sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;
		sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_16_BITS);
		break;
	default:
		return -EINVAL;
	}

	mmp_sspa_write_reg(sspa, sp_reg, sspa_sp);
	mmp_sspa_write_reg(sspa, ctrl_reg, sspa_ctrl);

	return 0;
}