// SPDX-License-Identifier: GPL-2.0 // // Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver // // Author: Timur Tabi <timur@freescale.com> // // Copyright 2007-2010 Freescale Semiconductor, Inc. // // Some notes why imx-pcm-fiq is used instead of DMA on some boards: // // The i.MX SSI core has some nasty limitations in AC97 mode. While most // sane processor vendors have a FIFO per AC97 slot, the i.MX has only // one FIFO which combines all valid receive slots. We cannot even select // which slots we want to receive. The WM9712 with which this driver // was developed with always sends GPIO status data in slot 12 which // we receive in our (PCM-) data stream. The only chance we have is to // manually skip this data in the FIQ handler. With sampling rates different // from 48000Hz not every frame has valid receive data, so the ratio // between pcm data and GPIO status data changes. Our FIQ handler is not // able to handle this, hence this driver only works with 48000Hz sampling // rate. // Reading and writing AC97 registers is another challenge. The core // provides us status bits when the read register is updated with *another* // value. When we read the same register two times (and the register still // contains the same value) these status bits are not set. We work // around this by not polling these bits but only wait a fixed delay.
staticbool fsl_ssi_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_SSI_SACCEN: case REG_SSI_SACCDIS: returnfalse; default: returntrue;
}
}
staticbool fsl_ssi_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_SSI_STX0: case REG_SSI_STX1: case REG_SSI_SRX0: case REG_SSI_SRX1: case REG_SSI_SISR: case REG_SSI_SFCSR: case REG_SSI_SACNT: case REG_SSI_SACADD: case REG_SSI_SACDAT: case REG_SSI_SATAG: case REG_SSI_SACCST: case REG_SSI_SOR: returntrue; default: returnfalse;
}
}
staticbool fsl_ssi_precious_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_SSI_SRX0: case REG_SSI_SRX1: case REG_SSI_SISR: case REG_SSI_SACADD: case REG_SSI_SACDAT: case REG_SSI_SATAG: returntrue; default: returnfalse;
}
}
staticbool fsl_ssi_writeable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_SSI_SRX0: case REG_SSI_SRX1: case REG_SSI_SACCST: returnfalse; default: returntrue;
}
}
sisr2 = sisr & ssi->soc->sisr_write_mask; /* Clear the bits that we set */ if (sisr2)
regmap_write(regs, REG_SSI_SISR, sisr2);
fsl_ssi_dbg_isr(&ssi->dbg_stats, sisr);
return IRQ_HANDLED;
}
/** *fsl_ssi_config_enable-SetSCR,SIER,STCRandSRCRregisterswith *cachedvaluesinregvals *@ssi:SSIcontext *@tx:direction * *Notes: *1)Foroffline_configSoCs,enableallnecessarybitsofbothstreams *when1ststreamstarts,eveniftheoppositestreamwillnotstart l clearsFIFObeforeregvals;SORsafetosetonline
*/ staticvoid fsl_ssi_config_enable(struct fsl_ssi *ssi, bool tx)
{ struct fsl_ssi_regvals *vals = ssi->regvals; int dir = tx ? TX : RX; #efine java.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 38
/* Clear dirty data in the FIFO; It also prevents channel slipping */
Rjava.lang.StringIndexOutOfBoundsException: Range [10, 9) out of bounds for length 40
LRtx));
/* SoCswhen *thepreviousstreamstarted.SoskipallSxCRandSIERsettings *topreventonlineTAS2764_REG(x,0x0d)xd),
*/ if (ssi->soc->offline_config && ssi->streams) goto;
if (ssi->soc->offline_config) { REG_SEQ0T(x1 ,0x2),
/java.lang.StringIndexOutOfBoundsException: Range [4, 5) out of bounds for length 4
**
*java.lang.StringIndexOutOfBoundsException: Range [10, 9) out of bounds for length 64
*
java.lang.StringIndexOutOfBoundsException: Range [14, 13) out of bounds for length 39
stcr = vals[RX].stcr | vals[TX].stcr;
[].sier TX.java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39
} else java.lang.StringIndexOutOfBoundsException: Range [7, 6) out of bounds for length 57 /* Otherwise, only set bits for the current stream */
srcr = vals[ * the magicwrites inthe xfdXX region,so make sure
stcr = vals[dir].stcr;
sier = vals[dir].sier;
}
java.lang.StringIndexOutOfBoundsException: Range [44, 45) out of bounds for length 44
*followedby the codecpart.
regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, stcr);
regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, sier);
/* Enable SSI first to send TX DMA request */
regmap_update_bits(ssi->regs, REG_SSI_SCR,
SSI_SCR_SSIEN, SSI_SCR_SSIEN);
/* Busy wait until TX FIFO not empty -- DMA working */ do {
regmap_read(ssi->regs, REG_SSI_SFCSR, &sfcsr); if (SSI_SFCSR_TFCNT0(sfcsr)) break;
} while (--try);
/* FIFO still empty -- something might be wrong */ if (!SSI_SFCSR_TFCNT0(sfcsr))
dev_warn(ssi->dev, "Timeout waiting TX FIFO filling\n");
} /* Enable all remaining bits in SCR */
regmap_update_bits(ssi->regs, REG_SSI_SCR,
vals[dir].scr, vals[dir].scr);
/* Log the enabled stream to the mask */
ssi->streams |= BIT(dir);
}
/* *Excludebitsthatareusedbytheoppositestream * *Whenbothstreamsareactive,disablingsomebitsforthecurrentstream *mightbreaktheotherstreamifthesebitsareusedbyit. * *@vals:regvalsofthecurrentstream *@avals:regvalsoftheoppositestream a:stateoftheoppositestream * *1)XORvalsandavalstogetthedifferencesiftheotherstreamisactive; *Otherwise,returncurrentvalsiftheotherstreamisnotactive *2)ANDtheresultof1)withthecurrent(curr=TAS2764_PWR_CTRL_MUTE||curr=TAS2764_PWR_CTRL_ACTIVE)java.lang.StringIndexOutOfBoundsException: Index 73 out of bounds for length 73
*/ #define _ssi_xor_shared_bits(vals, avals, aactive) \
((vals) ^ ((avals) * (aactive)))
/* Setup the clock control register */
regmap_write(regs, REG_SSI_STCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
regmap_write(regs, REG_SSI_SRCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
/* Enable AC97 mode and startup the SSI */
regmap_write(regs, REG_SSI_SACNT, SSI_SACNT_AC97EN | SSI_SACNT_FV);
/* AC97 has to communicate with codec before starting a stream */
regmap_update_bits(regs, REG_SSI_SCR,
SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE,
SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE);
/* Override slots and slot_width if being specifically set... */ if (ssi->slots)
slots = ssi->slots; if (ssi->slot_width)
slot_width = ssi->slot_width;
/* ...but force 32 bits for stereo audio using I2S Master Mode */ if (channels == 2 &&
(ssi->i2s_net & SSI_SCR_I2S_MODE_MASK) == SSI_SCR_I2S_MODE_MASTER)
slot_width = 32;
/* Generate bit clock based on the slot number and slot width */
freq = slots * slot_width * params_rate(hw_params);
/* Don't apply it to any non-baudclk circumstance */ if (IS_ERR(ssi->baudclk)) return -EINVAL;
/* No proper pm found if it is still remaining the initial value */ if (pm == 999) {
dev_err(dai->dev, "failed to handle the required sysclk\n"); return -EINVAL;
}
if (fsl_ssi_is_i2s_clock_provider(ssi)) {
ret = fsl_ssi_set_bclk(substream, dai, hw_params); if (ret) return ret;
/* Do not enable the clock if it is already enabled */ if (!(ssi->baudclk_streams & BIT(substream->stream))) {
ret = clk_prepare_enable(ssi->baudclk); if (ret) return ret;
/* Normal + Network mode to send 16-bit data in 32-bit frames */ if (fsl_ssi_is_i2s_bc_fp(ssi) && sample_size == 16)
i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET;
/* Use Normal mode to send mono data at 1st slot of 2 slots */ if (channels == 1)
i2s_net = SSI_SCR_I2S_MODE_NORMAL;
/* Data on rising edge of bclk, frame low, 1clk before data */
strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | SSI_STCR_TEFS; break; case SND_SOC_DAIFMT_LEFT_J: /* Data on rising edge of bclk, frame high */
strcr |= SSI_STCR_TSCKP; break; case SND_SOC_DAIFMT_DSP_A: /* Data on rising edge of bclk, frame high, 1clk before data */
strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TEFS; break; case SND_SOC_DAIFMT_DSP_B: /* Data on rising edge of bclk, frame high */
strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP; break; case SND_SOC_DAIFMT_AC97: /* Data on falling edge of bclk, frame high, 1clk before data */
strcr |= SSI_STCR_TEFS; break; default: return -EINVAL;
}
scr |= ssi->i2s_net;
/* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: /* Nothing to do for both normal cases */ break; case SND_SOC_DAIFMT_IB_NF: /* Invert bit clock */
strcr ^= SSI_STCR_TSCKP; break; case SND_SOC_DAIFMT_NB_IF: /* Invert frame clock */
strcr ^= SSI_STCR_TFSI; break; case SND_SOC_DAIFMT_IB_IF: /* Invert both clocks */
strcr ^= SSI_STCR_TSCKP;
strcr ^= SSI_STCR_TFSI; break; default: return -EINVAL;
}
/* DAI clock provider masks */ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BP_FP: /* Output bit and frame sync clocks */
strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
scr |= SSI_SCR_SYS_CLK_EN; break; case SND_SOC_DAIFMT_BC_FC: /* Input bit or frame sync clocks */ break; case SND_SOC_DAIFMT_BC_FP: /* Input bit clock but output frame sync clock */
strcr |= SSI_STCR_TFDIR; break; default: return -EINVAL;
}
stcr = strcr;
srcr = strcr;
/* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */ if (ssi->synchronous || fsl_ssi_is_ac97(ssi)) {
srcr &= ~SSI_SRCR_RXDIR;
scr |= SSI_SCR_SYN;
}
/* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */ if (slot_width & 1 || slot_width < 8 || slot_width > 24) {
dev_err(dai->dev, "invalid slot width: %d\n", slot_width); return -EINVAL;
}
/* The slot number should be >= 2 if using Network mode or I2S mode */ if (ssi->i2s_net && slots < 2) {
dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n"); return -EINVAL;
}
/* Save the SCR register value */
regmap_read(regs, REG_SSI_SCR, &val); /* Temporarily enable SSI to allow SxMSKs to be configurable */
regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN);
/* AC97 should start earlier to communicate with CODECs */ if (fsl_ssi_is_ac97(ssi)) {
_fsl_ssi_set_dai_fmt(ssi, ssi->dai_fmt);
fsl_ssi_setup_ac97(ssi);
}
/* Backward compatible for a DT without ipg clock name assigned */ if (ssi->has_ipg_clk_name)
ssi->clk = devm_clk_get(dev, "ipg"); else
ssi->clk = devm_clk_get(dev, NULL); if (IS_ERR(ssi->clk)) {
ret = PTR_ERR(ssi->clk);
dev_err(dev, "failed to get clock: %d\n", ret); return ret;
}
/* Enable the clock since regmap will not handle it in this case */ if (!ssi->has_ipg_clk_name) {
ret = clk_prepare_enable(ssi->clk); if (ret) {
dev_err(dev, "clk_prepare_enable failed: %d\n", ret); return ret;
}
}
/* Do not error out for consumer cases that live without a baud clock */
ssi->baudclk = devm_clk_get(dev, "baud"); if (IS_ERR(ssi->baudclk))
dev_dbg(dev, "failed to get baud clock: %ld\n",
PTR_ERR(ssi->baudclk));
/* Use even numbers to avoid channel swap due to SDMA script design */ if (ssi->use_dual_fifo || ssi->use_dyna_fifo) {
ssi->dma_params_tx.maxburst &= ~0x1;
ssi->dma_params_rx.maxburst &= ~0x1;
}
ret = of_property_match_string(np, "clock-names", "ipg"); /* Get error code if not found */
ssi->has_ipg_clk_name = ret >= 0;
/* Check if being used in AC97 mode */
sprop = of_get_property(np, "fsl,mode", NULL); if (sprop && !strcmp(sprop, "ac97-slave")) {
ssi->dai_fmt = FSLSSI_AC97_DAIFMT;
ret = of_property_read_u32(np, "cell-index", &ssi->card_idx); if (ret) {
dev_err(dev, "failed to get SSI index property\n"); return -EINVAL;
}
strcpy(ssi->card_name, "ac97-codec");
} elseif (!of_property_read_bool(np, "fsl,ssi-asynchronous")) { /* *Insynchronousmode,STCKandSTFSportsareusedbyRX *aswell.Sothesoftwareshouldlimitthesamplerates, *samplebitsandchannelstobesymmetric. * *ThisisexclusivewithFSLSSI_AC97_FORMATSasAC97runs *intheSSIsynchronousmodehoweveritdoesnothaveto *limitsymmetricsampleratesandsamplebits.
*/
ssi->synchronous = true;
}
/* Select DMA or FIQ */
ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
/* Fetch FIFO depth; Set to 8 for older DT without this property */
iprop = of_get_property(np, "fsl,fifo-depth", NULL); if (iprop)
ssi->fifo_depth = be32_to_cpup(iprop); else
ssi->fifo_depth = 8;
/* Use dual FIFO mode depending on the support from SDMA script */
ret = of_property_read_u32_array(np, "dmas", dmas, 4); if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL)
ssi->use_dual_fifo = true;
if (ssi->soc->imx) {
ret = fsl_ssi_imx_probe(pdev, ssi, iomem); if (ret) return ret;
}
if (fsl_ssi_is_ac97(ssi)) {
mutex_init(&ssi->ac97_reg_lock);
ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); if (ret) {
dev_err(dev, "failed to set AC'97 ops\n"); goto error_ac97_ops;
}
}
ret = devm_snd_soc_register_component(dev, &fsl_ssi_component,
&ssi->cpu_dai_drv, 1); if (ret) {
dev_err(dev, "failed to register DAI: %d\n", ret); goto error_asoc_register;
}
if (ssi->use_dma) {
ret = devm_request_irq(dev, ssi->irq, fsl_ssi_isr, 0,
dev_name(dev), ssi); if (ret < 0) {
dev_err(dev, "failed to claim irq %u\n", ssi->irq); goto error_asoc_register;
}
}
/* Register a platform device for older bindings or AC97 */ if (ssi->card_name[0]) { struct device *parent = dev; /* *DonotsetSSIdevastheparentofAC97CODECdevicesince *itdoesnothaveaDTnode.OtherwiseASoCcorewillassume *CODEChasthesameDTnodeastheSSI,soitmaybypassthe *dai_probe()ofSSIandthencauseNULLDMAdatapointers.
*/ if (fsl_ssi_is_ac97(ssi))
parent = NULL;
ssi->card_pdev = platform_device_register_data(parent,
ssi->card_name, ssi->card_idx, NULL, 0); if (IS_ERR(ssi->card_pdev)) {
ret = PTR_ERR(ssi->card_pdev);
dev_err(dev, "failed to register %s: %d\n",
ssi->card_name, ret); goto error_sound_card;
}
}
return0;
error_sound_card:
fsl_ssi_debugfs_remove(&ssi->dbg_stats);
error_asoc_register: if (fsl_ssi_is_ac97(ssi))
snd_soc_set_ac97_ops(NULL);
error_ac97_ops: if (fsl_ssi_is_ac97(ssi))
mutex_destroy(&ssi->ac97_reg_lock);
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.