switch (clk_id) { case PXA_SSP_CLK_NET_PLL:
sscr0 |= SSCR0_MOD; break; case PXA_SSP_CLK_PLL: /* Internal PLL is fixed */ if (ssp->type == PXA25x_SSP)
priv->sysclk = 1843200; else
priv->sysclk = 13000000; break; case PXA_SSP_CLK_EXT:
priv->sysclk = freq;
sscr0 |= SSCR0_ECS; break; case PXA_SSP_CLK_NET:
priv->sysclk = freq;
sscr0 |= SSCR0_NCS | SSCR0_MOD; break; case PXA_SSP_CLK_AUDIO:
priv->sysclk = 0;
pxa_ssp_set_scr(ssp, 1);
sscr0 |= SSCR0_ACS; break; default: return -ENODEV;
}
/* The SSP clock must be disabled when changing SSP clock mode
* on PXA2xx. On PXA3xx it must be enabled when doing so. */ if (ssp->type != PXA3xx_SSP)
clk_disable_unprepare(ssp->clk);
pxa_ssp_write_reg(ssp, SSCR0, sscr0); if (ssp->type != PXA3xx_SSP)
clk_prepare_enable(ssp->clk);
if (ssp->type == PXA3xx_SSP)
pxa_ssp_write_reg(ssp, SSACDD, 0);
switch (freq) { case 5622000: break; case 11345000:
ssacd |= (0x1 << 4); break; case 12235000:
ssacd |= (0x2 << 4); break; case 14857000:
ssacd |= (0x3 << 4); break; case 32842000:
ssacd |= (0x4 << 4); break; case 48000000:
ssacd |= (0x5 << 4); break; case 0: /* Disable */ break;
default: /* PXA3xx has a clock ditherer which can be used to generate * a wider range of frequencies - calculate a value for it.
*/ if (ssp->type == PXA3xx_SSP) {
u32 val;
u64 tmp = 19968;
tmp *= 1000000;
do_div(tmp, freq);
val = tmp;
val = (val << 16) | 64;
pxa_ssp_write_reg(ssp, SSACDD, val);
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BC_FC: case SND_SOC_DAIFMT_BC_FP: case SND_SOC_DAIFMT_BP_FP: break; default: return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: case SND_SOC_DAIFMT_NB_IF: case SND_SOC_DAIFMT_IB_IF: case SND_SOC_DAIFMT_IB_NF: break; default: return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: break;
default: return -EINVAL;
}
/* Settings will be applied in hw_params() */
priv->dai_fmt = fmt;
return 0;
}
/* * Set up the SSP DAI format. * The SSP Port must be inactive before calling this function as the * physical interface format is changed.
*/ staticint pxa_ssp_configure_dai_fmt(struct ssp_priv *priv)
{ struct ssp_device *ssp = priv->ssp;
u32 sscr0, sscr1, sspsp, scfr;
/* check if we need to change anything at all */ if (priv->configured_dai_fmt == priv->dai_fmt) return 0;
switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BC_FC: case SND_SOC_DAIFMT_BC_FP:
scfr = pxa_ssp_read_reg(ssp, SSCR1) | SSCR1_SCFR;
pxa_ssp_write_reg(ssp, SSCR1, scfr);
while (pxa_ssp_read_reg(ssp, SSSR) & SSSR_BSY)
cpu_relax(); break;
}
dump_registers(ssp);
/* Since we are configuring the timings for the format by hand * we have to defer some things until hw_params() where we * know parameters like the sample size.
*/
priv->configured_dai_fmt = priv->dai_fmt;
return 0;
}
struct pxa_ssp_clock_mode { int rate; int pll;
u8 acds;
u8 scdb;
};
/* Network mode with one active slot (ttsa == 1) can be used * to force 16-bit frame width on the wire (for S16_LE), even * with two channels. Use 16-bit DMA transfers for this case.
*/
pxa_ssp_set_dma_params(ssp,
((chn == 2) && (ttsa != 1)) || (width == 32),
substream->stream == SNDRV_PCM_STREAM_PLAYBACK, dma_data);
/* we can only change the settings if the port is not in use */ if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) return 0;
ret = pxa_ssp_configure_dai_fmt(priv); if (ret < 0) return ret;
/* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: if (ssp->type == PXA3xx_SSP)
sscr0 |= SSCR0_FPCKE;
sscr0 |= SSCR0_DataSize(16); break; case SNDRV_PCM_FORMAT_S24_LE:
sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8)); break; case SNDRV_PCM_FORMAT_S32_LE:
sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16)); break;
}
pxa_ssp_write_reg(ssp, SSCR0, sscr0);
if (sscr0 & SSCR0_ACS) {
ret = pxa_ssp_set_pll(priv, bclk);
/* * If we were able to generate the bclk directly, * all is fine. Otherwise, look up the closest rate * from the table and also set the dividers.
*/
if (ret < 0) { conststruct pxa_ssp_clock_mode *m; int ssacd;
for (m = pxa_ssp_clock_modes; m->rate; m++) { if (m->rate == rate) break;
}
if (!m->rate) return -EINVAL;
ret = pxa_ssp_set_pll(priv, bclk); if (ret < 0) return ret;
ssacd = pxa_ssp_read_reg(ssp, SSACD);
ssacd &= ~(SSACD_ACDS(7) | SSACD_SCDB_1X);
ssacd |= SSACD_ACDS(m->acds);
ssacd |= m->scdb;
pxa_ssp_write_reg(ssp, SSACD, ssacd);
}
} elseif (sscr0 & SSCR0_ECS) { /* * For setups with external clocking, the PLL and its diviers * are not active. Instead, the SCR bits in SSCR0 can be used * to divide the clock.
*/
pxa_ssp_set_scr(ssp, bclk / rate);
}
if (((priv->sysclk / bclk) == 64) && (width == 16)) { /* This is a special case where the bitclk is 64fs * and we're not dealing with 2*32 bits of audio * samples. * * The SSP values used for that are all found out by * trying and failing a lot; some of the registers * needed for that mode are only available on PXA3xx.
*/ if (ssp->type != PXA3xx_SSP) return -EINVAL;
sspsp |= SSPSP_SFRMWDTH(width * 2);
sspsp |= SSPSP_SFRMDLY(width * 4);
sspsp |= SSPSP_EDMYSTOP(3);
sspsp |= SSPSP_DMYSTOP(3);
sspsp |= SSPSP_DMYSTRT(1);
} else { /* The frame width is the width the LRCLK is * asserted for; the delay is expressed in * half cycle units. We need the extra cycle * because the data starts clocking out one BCLK * after LRCLK changes polarity.
*/
sspsp |= SSPSP_SFRMWDTH(width + 1);
sspsp |= SSPSP_SFRMDLY((width + 1) * 2);
sspsp |= SSPSP_DMYSTRT(1);
}
/* When we use a network mode, we always require TDM slots * - complain loudly and fail if they've not been set up yet.
*/ if ((sscr0 & SSCR0_MOD) && !ttsa) {
dev_err(ssp->dev, "No TDM timeslot configured\n"); return -EINVAL;
}
priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL); if (!priv) return -ENOMEM;
if (dev->of_node) { struct device_node *ssp_handle;
ssp_handle = of_parse_phandle(dev->of_node, "port", 0); if (!ssp_handle) {
dev_err(dev, "unable to get 'port' phandle\n");
ret = -ENODEV; goto err_priv;
}
priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio"); if (priv->ssp == NULL) {
ret = -ENODEV; goto err_priv;
}
priv->extclk = devm_clk_get(dev, "extclk"); if (IS_ERR(priv->extclk)) {
ret = PTR_ERR(priv->extclk); if (ret == -EPROBE_DEFER) goto err_priv;
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.