/* * 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; }
static void mmp_sspa_rx_disable(struct ssp_device *sspa) { unsigned int sspa_sp; sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP); sspa_sp &= ~SSPA_SP_S_EN; sspa_sp |= SSPA_SP_WEN; mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); }
static void mmp_sspa_tx_enable(struct ssp_device *sspa) { unsigned int sspa_sp; sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP); sspa_sp &= ~SSPA_SP_S_RST; sspa_sp |= SSPA_SP_S_EN; mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); }
/* * 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; /* check if we need to change anything at all */ if (sspa_priv->dai_fmt == fmt) return 0; /* we can only change the settings if the port is not in use */ if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) || (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) { dev_err(&sspa->pdev->dev, "can't change hardware dai format: stream is in use\n"); return -EINVAL; } /* reset port settings */ sspa_sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH; 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; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: sspa_sp |= SSPA_SP_FSP; 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); break; default: return -EINVAL; } mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); sspa_sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH); mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp); /* * FIXME: hw issue, for the tx serial port, * can not config the master/slave mode; * so must clean this bit. * The master/slave mode has been set in the * rx port. */ sspa_sp &= ~SSPA_SP_MSL; mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp); mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl); mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl); /* Since we are configuring the timings for the format by hand * we have to defer some things until hw_params() where we * know parameters like the sample size. */ sspa_priv->dai_fmt = fmt; 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; }