// SPDX-License-Identifier: GPL-2.0-only /* * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor * * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> * * DT support (c) 2016 Petr Kulhavy, Barix AG <petr@barix.com> * based on davinci-mcasp.c DT support * * TODO: * on DA850 implement HW FIFOs instead of DMA into DXR and DRR registers
*/
/* * NOTE: terminology here is confusing. * * - This driver supports the "Audio Serial Port" (ASP), * found on dm6446, dm355, and other DaVinci chips. * * - But it labels it a "Multi-channel Buffered Serial Port" * (McBSP) as on older chips like the dm642 ... which was * backward-compatible, possibly explaining that confusion. * * - OMAP chips have a controller called McBSP, which is * incompatible with the DaVinci flavor of McBSP. * * - Newer DaVinci chips have a controller called McASP, * incompatible with ASP and with either McBSP. * * In short: this uses ASP to implement I2S, not McBSP. * And it won't be the only DaVinci implemention of I2S.
*/ #define DAVINCI_MCBSP_DRR_REG 0x00 #define DAVINCI_MCBSP_DXR_REG 0x04 #define DAVINCI_MCBSP_SPCR_REG 0x08 #define DAVINCI_MCBSP_RCR_REG 0x0c #define DAVINCI_MCBSP_XCR_REG 0x10 #define DAVINCI_MCBSP_SRGR_REG 0x14 #define DAVINCI_MCBSP_PCR_REG 0x24
struct davinci_mcbsp_dev { struct device *dev; struct snd_dmaengine_dai_dma_data dma_data[2]; int dma_request[2]; void __iomem *base; #define MOD_DSP_A 0 #define MOD_DSP_B 1 int mode;
u32 pcr; struct clk *clk; struct clk *ext_clk; /* * Combining both channels into 1 element will at least double the * amount of time between servicing the dma channel, increase * effiency, and reduce the chance of overrun/underrun. But, * it will result in the left & right channels being swapped. * * If relabeling the left and right channels is not possible, * you may want to let the codec know to swap them back. * * It may allow x10 the amount of time to service dma requests, * if the codec is master and is using an unnecessarily fast bit clock * (ie. tlvaic23b), independent of the sample rate. So, having an * entire frame at once means it can be serviced at the sample rate * instead of the bit clock rate. * * In the now unlikely case that an underrun still * occurs, both the left and right samples will be repeated * so that no pops are heard, and the left and right channels * won't end up being swapped because of the underrun.
*/ unsigned enable_channel_combine:1;
unsignedint fmt; int clk_div; bool i2s_accurate_sck;
staticvoid toggle_clock(struct davinci_mcbsp_dev *dev, int playback)
{
u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP; /* The clock needs to toggle to complete reset. * So, fake it by toggling the clk polarity.
*/
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m);
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr);
}
/* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BP_FP: /* cpu is master */
pcr = DAVINCI_MCBSP_PCR_FSXM |
DAVINCI_MCBSP_PCR_FSRM |
DAVINCI_MCBSP_PCR_CLKXM |
DAVINCI_MCBSP_PCR_CLKRM; break; case SND_SOC_DAIFMT_BC_FP: if (dev->tdm_slots || dev->slot_width) {
dev_err(dev->dev, "TDM is not supported for BC_FP format\n"); return -EINVAL;
}
/* * McBSP CLKR pin is the input for the Sample Rate Generator. * McBSP FSR and FSX are driven by the Sample Rate Generator.
*/
pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
pcr |= DAVINCI_MCBSP_PCR_SCLKME; break; case SND_SOC_DAIFMT_BP_FC: /* cpu is bitclock provider */
pcr = DAVINCI_MCBSP_PCR_CLKXM |
DAVINCI_MCBSP_PCR_CLKRM; break;
case SND_SOC_DAIFMT_BC_FC: if (dev->tdm_slots || dev->slot_width) {
dev_err(dev->dev, "TDM is not supported for BC_FC format\n"); return -EINVAL;
}
/* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* Davinci doesn't support TRUE I2S, but some codecs will have * the left and right channels contiguous. This allows * dsp_a mode to be used with an inverted normal frame clk. * If your codec is master and does not have contiguous * channels, then you will have sound on only one channel. * Try using a different mode, or codec as slave. * * The TLV320AIC33 is an example of a codec where this works. * It has a variable bit clock frequency allowing it to have * valid data on every bit clock. * * The TLV320AIC23 is an example of a codec where this does not * work. It has a fixed bit clock frequency with progressively * more empty bit clock slots between channels as the sample * rate is lowered.
*/
inv_fs = true;
fallthrough; case SND_SOC_DAIFMT_DSP_A:
dev->mode = MOD_DSP_A; break; case SND_SOC_DAIFMT_DSP_B:
dev->mode = MOD_DSP_B; break; default:
printk(KERN_ERR "%s:bad format\n", __func__); return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: /* CLKRP Receive clock polarity, * 1 - sampled on rising edge of CLKR * valid on rising edge * CLKXP Transmit clock polarity, * 1 - clocked on falling edge of CLKX * valid on rising edge * FSRP Receive frame sync pol, 0 - active high * FSXP Transmit frame sync pol, 0 - active high
*/
pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP); break; case SND_SOC_DAIFMT_IB_IF: /* CLKRP Receive clock polarity, * 0 - sampled on falling edge of CLKR * valid on falling edge * CLKXP Transmit clock polarity, * 0 - clocked on rising edge of CLKX * valid on falling edge * FSRP Receive frame sync pol, 1 - active low * FSXP Transmit frame sync pol, 1 - active low
*/
pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP); break; case SND_SOC_DAIFMT_NB_IF: /* CLKRP Receive clock polarity, * 1 - sampled on rising edge of CLKR * valid on rising edge * CLKXP Transmit clock polarity, * 1 - clocked on falling edge of CLKX * valid on rising edge * FSRP Receive frame sync pol, 1 - active low * FSXP Transmit frame sync pol, 1 - active low
*/
pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP); break; case SND_SOC_DAIFMT_IB_NF: /* CLKRP Receive clock polarity, * 0 - sampled on falling edge of CLKR * valid on falling edge * CLKXP Transmit clock polarity, * 0 - clocked on rising edge of CLKX * valid on falling edge * FSRP Receive frame sync pol, 0 - active high * FSXP Transmit frame sync pol, 0 - active high
*/ break; default: return -EINVAL;
} if (inv_fs == true)
pcr ^= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
dev->pcr = pcr;
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr); return 0;
}
staticint davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div)
{ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
if (div_id != DAVINCI_MCBSP_CLKGDV) return -ENODEV;
if (playback) { /* Enable the transmitter */
spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
spcr |= DAVINCI_MCBSP_SPCR_XRST;
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
/* wait for any unexpected frame sync error to occur */
udelay(100);
/* Disable the transmitter to clear any outstanding XSYNCERR */
spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
spcr &= ~DAVINCI_MCBSP_SPCR_XRST;
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
toggle_clock(dev, playback);
}
return 0;
}
staticint davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); int ret = 0; int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
davinci_mcbsp_start(dev, substream); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
davinci_mcbsp_stop(dev, playback); break; default:
ret = -EINVAL;
} return ret;
}
mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); if (!mem) {
dev_warn(&pdev->dev, "\"mpu\" mem resource not found, using index 0\n");
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) {
dev_err(&pdev->dev, "no mem resource?\n"); return -ENODEV;
}
}
io_base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(io_base)) return PTR_ERR(io_base);
dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcbsp_dev),
GFP_KERNEL); if (!dev) return -ENOMEM;
/* * The optional is there for backward compatibility. * If 'fck' is not present, the clk_get(dev, NULL) that follows may find something
*/
dev->clk = devm_clk_get_optional(&pdev->dev, "fck"); if (IS_ERR(dev->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk), "Invalid functional clock\n"); if (!dev->clk) {
dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk), "Missing functional clock\n");
}
ret = clk_prepare_enable(dev->clk); if (ret) return ret;
if (dev->ext_clk) {
dev_dbg(&pdev->dev, "External clock used for sample rate generator\n");
ret = clk_prepare_enable(dev->ext_clk); if (ret) {
dev_err_probe(&pdev->dev, ret, "Failed to enable external clock\n"); goto err_disable_clk;
}
}
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.