/* Index list for the values that has if (DPLL Locked) condition */ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb }; #define SRPC_NODPLL_START1 0x5 #define SRPC_NODPLL_START2 0xc
#define DEFAULT_RXCLK_SRC 1
#define RX_SAMPLE_RATE_KCONTROL "RX Sample Rate"
/** * struct fsl_spdif_soc_data: soc specific data * * @imx: for imx platform * @shared_root_clock: flag of sharing a clock source with others; * so the driver shouldn't set root clock rate * @raw_capture_mode: if raw capture mode support * @cchannel_192b: if there are registers for 192bits C channel data * @interrupts: interrupt number * @tx_burst: tx maxburst size * @rx_burst: rx maxburst size * @tx_formats: tx supported data format
*/ struct fsl_spdif_soc_data { bool imx; bool shared_root_clock; bool raw_capture_mode; bool cchannel_192b;
u32 interrupts;
u32 tx_burst;
u32 rx_burst;
u64 tx_formats;
};
/* * SPDIF control structure * Defines channel status, subcode and Q sub
*/ struct spdif_mixer_control { /* spinlock to access control data */
spinlock_t ctl_lock;
/* IEC958 channel tx status bit */ unsignedchar ch_status[4];
/* User bits */ unsignedchar subcode[2 * SPDIF_UBITS_SIZE];
/* Q subcode part of user bits */ unsignedchar qsub[2 * SPDIF_QSUB_SIZE];
/* Buffer offset for U/Q */
u32 upos;
u32 qpos;
/* Ready buffer index of the two buffers */
u32 ready_buf;
};
/** * struct fsl_spdif_priv - Freescale SPDIF private data * @soc: SPDIF soc data * @fsl_spdif_control: SPDIF control data * @cpu_dai_drv: cpu dai driver * @snd_card: sound card pointer * @rxrate_kcontrol: kcontrol for RX Sample Rate * @pdev: platform device pointer * @regmap: regmap handler * @dpll_locked: dpll lock flag * @txrate: the best rates for playback * @txclk_df: STC_TXCLK_DF dividers value for playback * @sysclk_df: STC_SYSCLK_DF dividers value for playback * @txclk_src: STC_TXCLK_SRC values for playback * @rxclk_src: SRPC_CLKSRC_SEL values for capture * @txclk: tx clock sources for playback * @rxclk: rx clock sources for capture * @coreclk: core clock for register access via DMA * @sysclk: system clock for rx clock rate measurement * @spbaclk: SPBA clock (optional, depending on SoC design) * @dma_params_tx: DMA parameters for transmit channel * @dma_params_rx: DMA parameters for receive channel * @regcache_srpc: regcache for SRPC * @bypass: status of bypass input to output * @pll8k_clk: PLL clock for the rate of multiply of 8kHz * @pll11k_clk: PLL clock for the rate of multiply of 11kHz
*/ struct fsl_spdif_priv { conststruct fsl_spdif_soc_data *soc; struct spdif_mixer_control fsl_spdif_control; struct snd_soc_dai_driver cpu_dai_drv; struct snd_card *snd_card; struct snd_kcontrol *rxrate_kcontrol; struct platform_device *pdev; struct regmap *regmap; bool dpll_locked;
u32 txrate[SPDIF_TXRATE_MAX];
u8 txclk_df[SPDIF_TXRATE_MAX];
u16 sysclk_df[SPDIF_TXRATE_MAX];
u8 txclk_src[SPDIF_TXRATE_MAX];
u8 rxclk_src; struct clk *txclk[STC_TXCLK_SRC_MAX]; struct clk *rxclk; struct clk *coreclk; struct clk *sysclk; struct clk *spbaclk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; /* regcache for SRPC */
u32 regcache_srpc; bool bypass; struct clk *pll8k_clk; struct clk *pll11k_clk;
};
/* Check if clk is a root clock that does not share clock source with others */ staticinlinebool fsl_spdif_can_set_clk_rate(struct fsl_spdif_priv *spdif, int clk)
{ return (clk == STC_TXCLK_SPDIF_ROOT) && !spdif->soc->shared_root_clock;
}
dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n");
/* Clear illegal symbol if DPLL unlocked since no audio stream */ if (!spdif_priv->dpll_locked)
regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0);
}
/* * RESET bit would be cleared after finishing its reset procedure, * which typically lasts 8 cycles. 1000 cycles will keep it safe.
*/ do {
regmap_read(regmap, REG_SPDIF_SCR, &val);
} while ((val & SCR_SOFT_RESET) && cycle--);
/* * The first 32bit should be in REG_SPDIF_STCCA_31_0 register, * but here we need to set REG_SPDIF_STCCA_191_160 on 8ULP * then can get correct result with HDMI analyzer capture. * There is a hardware bug here.
*/
regmap_write(regmap, REG_SPDIF_STCCA_191_160, ch_status);
}
}
/* Set SPDIF PhaseConfig register for rx clock */ staticint spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv, enum spdif_gainsel gainsel, int dpll_locked)
{ struct regmap *regmap = spdif_priv->regmap;
u8 clksrc = spdif_priv->rxclk_src;
if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX) return -EINVAL;
ret = fsl_spdif_probe_txclk(spdif_priv, rate); if (ret) return ret;
clk = spdif_priv->txclk_src[rate]; if (clk >= STC_TXCLK_SRC_MAX) {
dev_err(&pdev->dev, "tx clock source is out of range\n"); return -EINVAL;
}
txclk_df = spdif_priv->txclk_df[rate]; if (txclk_df == 0) {
dev_err(&pdev->dev, "the txclk_df can't be zero\n"); return -EINVAL;
}
sysclk_df = spdif_priv->sysclk_df[rate];
if (!fsl_spdif_can_set_clk_rate(spdif_priv, clk)) goto clk_set_bypass;
/* The S/PDIF block needs a clock of 64 * fs * txclk_df */
ret = clk_set_rate(spdif_priv->txclk[clk],
64 * sample_rate * txclk_df); if (ret) {
dev_err(&pdev->dev, "failed to set tx clock rate\n"); return ret;
}
/* Reset module and interrupts only for first initialization */ if (!snd_soc_dai_active(cpu_dai)) {
ret = spdif_softreset(spdif_priv); if (ret) {
dev_err(&pdev->dev, "failed to soft reset\n"); return ret;
}
/* Disable all the interrupts */
regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0);
}
/* Power down SPDIF module only if tx&rx are both inactive */ if (!snd_soc_dai_active(cpu_dai)) {
spdif_intr_status_clear(spdif_priv);
regmap_update_bits(regmap, REG_SPDIF_SCR,
SCR_LOW_POWER, SCR_LOW_POWER);
}
}
/* Reparent clock if required condition is true */ if (!fsl_spdif_can_set_clk_rate(spdif_priv, STC_TXCLK_SPDIF_ROOT)) return 0;
/* Get root clock */
clk = spdif_priv->txclk[STC_TXCLK_SPDIF_ROOT];
/* Disable clock first, for it was enabled by pm_runtime */
clk_disable_unprepare(clk);
fsl_asoc_reparent_pll_clocks(&pdev->dev, clk, spdif_priv->pll8k_clk,
spdif_priv->pll11k_clk, sample_rate);
ret = clk_prepare_enable(clk); if (ret) return ret;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
regmap_update_bits(regmap, REG_SPDIF_SIE, intr, intr);
regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, dmaen); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0);
regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0);
regmap_write(regmap, REG_SPDIF_STL, 0x0);
regmap_write(regmap, REG_SPDIF_STR, 0x0); break; default: return -EINVAL;
}
return 0;
}
/* * FSL SPDIF IEC958 controller(mixer) functions * * Channel status get/put control * User bit value get/put control * Valid bit value get control * DPLL lock status get control * User bit sync mode selection control
*/
/* * Get DPLL lock or not info from stable interrupt status register. * User application must use this control to get locked, * then can do next PCM operation
*/ staticint fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); int rate = 0;
if (spdif_priv->dpll_locked)
rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
ucontrol->value.integer.value[0] = rate;
return 0;
}
/* * User bit sync mode: * 1 CD User channel subcode * 0 Non-CD data
*/ staticint fsl_spdif_usync_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct regmap *regmap = spdif_priv->regmap;
u32 val;
staticbool fsl_spdif_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_SPDIF_SCR: case REG_SPDIF_SRCD: case REG_SPDIF_SRPC: case REG_SPDIF_SIE: case REG_SPDIF_SIS: case REG_SPDIF_SRL: case REG_SPDIF_SRR: case REG_SPDIF_SRCSH: case REG_SPDIF_SRCSL: case REG_SPDIF_SRU: case REG_SPDIF_SRQ: case REG_SPDIF_STCSCH: case REG_SPDIF_STCSCL: case REG_SPDIF_STCSPH: case REG_SPDIF_STCSPL: case REG_SPDIF_SRFM: case REG_SPDIF_STC: case REG_SPDIF_SRCCA_31_0: case REG_SPDIF_SRCCA_63_32: case REG_SPDIF_SRCCA_95_64: case REG_SPDIF_SRCCA_127_96: case REG_SPDIF_SRCCA_159_128: case REG_SPDIF_SRCCA_191_160: case REG_SPDIF_STCCA_31_0: case REG_SPDIF_STCCA_63_32: case REG_SPDIF_STCCA_95_64: case REG_SPDIF_STCCA_127_96: case REG_SPDIF_STCCA_159_128: case REG_SPDIF_STCCA_191_160: returntrue; default: returnfalse;
}
}
staticbool fsl_spdif_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_SPDIF_SRPC: case REG_SPDIF_SIS: case REG_SPDIF_SRL: case REG_SPDIF_SRR: case REG_SPDIF_SRCSH: case REG_SPDIF_SRCSL: case REG_SPDIF_SRU: case REG_SPDIF_SRQ: case REG_SPDIF_SRFM: case REG_SPDIF_SRCCA_31_0: case REG_SPDIF_SRCCA_63_32: case REG_SPDIF_SRCCA_95_64: case REG_SPDIF_SRCCA_127_96: case REG_SPDIF_SRCCA_159_128: case REG_SPDIF_SRCCA_191_160: returntrue; default: returnfalse;
}
}
staticbool fsl_spdif_writeable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case REG_SPDIF_SCR: case REG_SPDIF_SRCD: case REG_SPDIF_SRPC: case REG_SPDIF_SIE: case REG_SPDIF_SIC: case REG_SPDIF_STL: case REG_SPDIF_STR: case REG_SPDIF_STCSCH: case REG_SPDIF_STCSCL: case REG_SPDIF_STCSPH: case REG_SPDIF_STCSPL: case REG_SPDIF_STC: case REG_SPDIF_STCCA_31_0: case REG_SPDIF_STCCA_63_32: case REG_SPDIF_STCCA_95_64: case REG_SPDIF_STCCA_127_96: case REG_SPDIF_STCCA_159_128: case REG_SPDIF_STCCA_191_160: returntrue; default: returnfalse;
}
}
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
clk = spdif_priv->txclk[i]; if (IS_ERR(clk)) {
dev_err(dev, "no rxtx%d clock in devicetree\n", i); return PTR_ERR(clk);
} if (!clk_get_rate(clk)) continue;
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
fsl_spdif_can_set_clk_rate(spdif_priv, i)); if (savesub == ret) continue;
savesub = ret;
spdif_priv->txclk_src[index] = i;
/* To quick catch a divisor, we allow a 0.1% deviation */ if (savesub < 100) break;
}
dev_dbg(dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
spdif_priv->txclk_src[index], rate[index]);
dev_dbg(dev, "use txclk df %d for %dHz sample rate\n",
spdif_priv->txclk_df[index], rate[index]); if (clk_is_match(spdif_priv->txclk[spdif_priv->txclk_src[index]], spdif_priv->sysclk))
dev_dbg(dev, "use sysclk df %d for %dHz sample rate\n",
spdif_priv->sysclk_df[index], rate[index]);
dev_dbg(dev, "the best rate for %dHz sample rate is %dHz\n",
rate[index], spdif_priv->txrate[index]);
/* Initialize this copy of the CPU DAI driver structure */
memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
spdif_priv->cpu_dai_drv.name = dev_name(&pdev->dev);
spdif_priv->cpu_dai_drv.playback.formats =
spdif_priv->soc->tx_formats;
/* Get the addresses and IRQ */
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) return PTR_ERR(regs);
for (i = 0; i < spdif_priv->soc->interrupts; i++) {
irq = platform_get_irq(pdev, i); if (irq < 0) return irq;
ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
dev_name(&pdev->dev), spdif_priv); if (ret) {
dev_err(&pdev->dev, "could not claim irq %u\n", irq); return ret;
}
}
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
sprintf(tmp, "rxtx%d", i);
spdif_priv->txclk[i] = devm_clk_get(&pdev->dev, tmp); if (IS_ERR(spdif_priv->txclk[i])) {
dev_err(&pdev->dev, "no rxtx%d clock in devicetree\n", i); return PTR_ERR(spdif_priv->txclk[i]);
}
}
/* Get system clock for rx clock rate calculation */
spdif_priv->sysclk = spdif_priv->txclk[5]; if (IS_ERR(spdif_priv->sysclk)) {
dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n"); return PTR_ERR(spdif_priv->sysclk);
}
/* Get core clock for data register access via DMA */
spdif_priv->coreclk = devm_clk_get(&pdev->dev, "core"); if (IS_ERR(spdif_priv->coreclk)) {
dev_err(&pdev->dev, "no core clock in devicetree\n"); return PTR_ERR(spdif_priv->coreclk);
}
spdif_priv->spbaclk = devm_clk_get(&pdev->dev, "spba"); if (IS_ERR(spdif_priv->spbaclk))
dev_warn(&pdev->dev, "no spba clock in devicetree\n");
/* Select clock source for rx/tx clock */
spdif_priv->rxclk = spdif_priv->txclk[1]; if (IS_ERR(spdif_priv->rxclk)) {
dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n"); return PTR_ERR(spdif_priv->rxclk);
}
spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
/* Register with ASoC */
dev_set_drvdata(&pdev->dev, spdif_priv);
pm_runtime_enable(&pdev->dev);
regcache_cache_only(spdif_priv->regmap, true);
/* * Register platform component before registering cpu dai for there * is not defer probe for platform component in snd_soc_add_pcm_runtime().
*/
ret = imx_pcm_dma_init(pdev); if (ret) {
dev_err_probe(&pdev->dev, ret, "imx_pcm_dma_init failed\n"); goto err_pm_disable;
}
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_spdif_component,
&spdif_priv->cpu_dai_drv, 1); if (ret) {
dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); goto err_pm_disable;
}
for (i = 0; i < STC_TXCLK_SRC_MAX; i++)
clk_disable_unprepare(spdif_priv->txclk[i]);
if (!IS_ERR(spdif_priv->spbaclk))
clk_disable_unprepare(spdif_priv->spbaclk);
clk_disable_unprepare(spdif_priv->coreclk);
return 0;
}
staticint fsl_spdif_runtime_resume(struct device *dev)
{ struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); int ret; int i;
ret = clk_prepare_enable(spdif_priv->coreclk); if (ret) {
dev_err(dev, "failed to enable core clock\n"); return ret;
}
if (!IS_ERR(spdif_priv->spbaclk)) {
ret = clk_prepare_enable(spdif_priv->spbaclk); if (ret) {
dev_err(dev, "failed to enable spba clock\n"); goto disable_core_clk;
}
}
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
ret = clk_prepare_enable(spdif_priv->txclk[i]); if (ret) goto disable_tx_clk;
}
ret = regcache_sync(spdif_priv->regmap); if (ret) goto disable_tx_clk;
return 0;
disable_tx_clk: for (i--; i >= 0; i--)
clk_disable_unprepare(spdif_priv->txclk[i]); if (!IS_ERR(spdif_priv->spbaclk))
clk_disable_unprepare(spdif_priv->spbaclk);
disable_core_clk:
clk_disable_unprepare(spdif_priv->coreclk);
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.