/* check if the PSC is already streaming data */
stat = __raw_readl(I2S_STAT(pscdata)); if (stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB)) { /* reject parameters not currently set up in hardware */
cfgbits = __raw_readl(I2S_CFG(pscdata)); if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) ||
(params_rate(params) != pscdata->rate)) return -EINVAL;
} else { /* set sample bitdepth */
pscdata->cfg &= ~(0x1f << 4);
pscdata->cfg |= PSC_I2SCFG_SET_LEN(params->msbits); /* remember current rate for other stream */
pscdata->rate = params_rate(params);
} return 0;
}
/* Configure PSC late: on my devel systems the codec is I2S master and * supplies the i2sbitclock __AND__ i2sMclk (!) to the PSC unit. ASoC * uses aggressive PM and switches the codec off when it is not in use * which also means the PSC unit doesn't get any clocks and is therefore * dead. That's why this chunk here gets called from the trigger callback * because I can be reasonably certain the codec is driving the clocks.
*/ staticint au1xpsc_i2s_configure(struct au1xpsc_audio_data *pscdata)
{ unsignedlong tmo;
/* bring PSC out of sleep, and configure I2S unit */
__raw_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
wmb(); /* drain writebuffer */
tmo = 1000000; while (!(__raw_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo)
tmo--;
staticint au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype)
{ unsignedlong tmo, stat; int ret;
ret = 0;
/* if both TX and RX are idle, configure the PSC */
stat = __raw_readl(I2S_STAT(pscdata)); if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
ret = au1xpsc_i2s_configure(pscdata); if (ret) goto out;
}
/* wait for stop confirmation */
tmo = 1000000; while ((__raw_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
tmo--;
/* if both TX and RX are idle, disable PSC */
stat = __raw_readl(I2S_STAT(pscdata)); if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
__raw_writel(0, I2S_CFG(pscdata));
wmb(); /* drain writebuffer */
__raw_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
wmb(); /* drain writebuffer */
} return 0;
}
staticint au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{ struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); int ret, stype = substream->stream;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME:
ret = au1xpsc_i2s_start(pscdata, stype); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND:
ret = au1xpsc_i2s_stop(pscdata, stype); break; default:
ret = -EINVAL;
} return ret;
}
/* preserve PSC clock source set up by platform (dev.platform_data * is already occupied by soc layer)
*/
sel = __raw_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
wmb(); /* drain writebuffer */
__raw_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd));
__raw_writel(0, I2S_CFG(wd));
wmb(); /* drain writebuffer */
/* preconfigure: set max rx/tx fifo depths */
wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
/* don't wait for I2S core to become ready now; clocks may not * be running yet; depending on clock input for PSC a wait might * time out.
*/
/* name the DAI like this device instance ("au1xpsc-i2s.PSCINDEX") */
memcpy(&wd->dai_drv, &au1xpsc_i2s_dai_template, sizeof(struct snd_soc_dai_driver));
wd->dai_drv.name = dev_name(&pdev->dev);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.