priv->pll_config = devm_platform_ioremap_resource_byname(pdev, "pll_regs"); if (IS_ERR(priv->pll_config)) return -ENOMEM;
priv->soc_control = devm_platform_ioremap_resource_byname(pdev, "soc_ctrl"); if (IS_ERR(priv->soc_control)) return -ENOMEM;
/* Select one of exceptive modes: I2S or S/PDIF */
reg_val = readl(priv->soc_control); if (of_property_read_bool(np, "spdif-mode")) {
reg_val |= A38X_SPDIF_MODE_ENABLE;
dev_info(&pdev->dev, "using S/PDIF mode\n");
} else {
reg_val &= ~A38X_SPDIF_MODE_ENABLE;
dev_info(&pdev->dev, "using I2S mode\n");
}
writel(reg_val, priv->soc_control);
/* Update available rates of mclk's fs */ for (i = 0; i < 2; i++) {
dai_drv[i].playback.rates |= SNDRV_PCM_RATE_192000;
dai_drv[i].capture.rates |= SNDRV_PCM_RATE_192000;
}
/* Set frequency offset value to not valid and enable PLL reset */
reg_val = readl(base + A38X_PLL_CONF_REG1);
reg_val &= ~A38X_PLL_FREQ_OFFSET_VALID;
reg_val &= ~A38X_PLL_SW_RESET;
writel(reg_val, base + A38X_PLL_CONF_REG1);
udelay(1);
/* Update PLL parameters */ switch (rate) { default: case 44100:
freq_offset = 0x735;
fb_clk_div = 0x1b;
audio_postdiv = 0xc; break; case 48000:
audio_postdiv = 0xc; break; case 96000:
audio_postdiv = 0x6; break; case 192000:
audio_postdiv = 0x3; break;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J:
mask = KIRKWOOD_I2S_CTL_RJ; break; case SND_SOC_DAIFMT_LEFT_J:
mask = KIRKWOOD_I2S_CTL_LJ; break; case SND_SOC_DAIFMT_I2S:
mask = KIRKWOOD_I2S_CTL_I2S; break; default: return -EINVAL;
}
/* * Set same format for playback and record * This avoids some troubles.
*/
value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
value |= mask;
writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
value |= mask;
writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
value = KIRKWOOD_DCO_CTL_OFFSET_0; switch (rate) { default: case 44100:
value |= KIRKWOOD_DCO_CTL_FREQ_11; break; case 48000:
value |= KIRKWOOD_DCO_CTL_FREQ_12; break; case 96000:
value |= KIRKWOOD_DCO_CTL_FREQ_24; break;
}
writel(value, io + KIRKWOOD_DCO_CTL);
/* wait for dco locked */ do {
cpu_relax();
value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK;
} while (value == 0);
}
if (IS_ERR(priv->extclk)) { /* use internal dco for the supported rates
* defined in kirkwood_i2s_dai */
dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
__func__, rate); if (priv->pll_config)
armada_38x_set_pll(priv->pll_config, rate); else
kirkwood_set_dco(priv->io, rate);
clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
} else { /* use the external clock for the other rates
* defined in kirkwood_i2s_dai_extclk */
dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n",
__func__, rate, 256 * rate);
clk_set_rate(priv->extclk, 256 * rate);
ctl = readl(priv->io + KIRKWOOD_PLAYCTL); if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) { unsigned timeout = 5000; /* * The Armada510 spec says that if we enter pause mode, the * busy bit must be read back as clear _twice_. Make sure * we respect that otherwise we get DMA underruns.
*/ do {
value = ctl;
ctl = readl(priv->io + KIRKWOOD_PLAYCTL); if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)) break;
udelay(1);
} while (timeout--);
if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)
dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n",
ctl);
}
value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_RECCTL);
/* enable interrupts */
value = readl(priv->io + KIRKWOOD_INT_MASK);
value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
writel(value, priv->io + KIRKWOOD_INT_MASK);
/* enable record */
writel(ctl, priv->io + KIRKWOOD_RECCTL); break;
case SNDRV_PCM_TRIGGER_STOP: /* stop audio, disable interrupts */
value = readl(priv->io + KIRKWOOD_RECCTL);
value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
writel(value, priv->io + KIRKWOOD_RECCTL);
value = readl(priv->io + KIRKWOOD_INT_MASK);
value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
writel(value, priv->io + KIRKWOOD_INT_MASK);
/* disable all records */
value = readl(priv->io + KIRKWOOD_RECCTL);
value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_RECCTL); break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND:
value = readl(priv->io + KIRKWOOD_RECCTL);
value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
writel(value, priv->io + KIRKWOOD_RECCTL); break;
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
value = readl(priv->io + KIRKWOOD_RECCTL);
value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
writel(value, priv->io + KIRKWOOD_RECCTL); break;
/* put system in a "safe" state : */ /* disable audio interrupts */
writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
writel(0, priv->io + KIRKWOOD_INT_MASK);
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.