if (irqst_spcr1 & RSYNC_ERR) {
dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n",
irqst_spcr1); /* Writing zero to RSYNC_ERR clears the IRQ */
MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1));
}
return IRQ_HANDLED;
}
/* * omap_mcbsp_config simply write a config to the * appropriate McBSP. * You either call this function or set the McBSP registers * by yourself before calling omap_mcbsp_start().
*/ staticvoid omap_mcbsp_config(struct omap_mcbsp *mcbsp, conststruct omap_mcbsp_reg_cfg *config)
{
dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
mcbsp->id, mcbsp->phys_base);
/** * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register * @mcbsp: omap_mcbsp struct for the McBSP instance * @stream: Stream direction (playback/capture) * * Returns the address of mcbsp data transmit register or data receive register * to be used by DMA for transferring/receiving data
*/ staticint omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, unsignedint stream)
{ int data_reg;
/* * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. * The threshold parameter is 1 based, and it is converted (threshold - 1) * for the THRSH2 register.
*/ staticvoid omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
{ if (threshold && threshold <= mcbsp->max_tx_thres)
MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
}
/* * omap_mcbsp_set_rx_threshold configures the receive threshold in words. * The threshold parameter is 1 based, and it is converted (threshold - 1) * for the THRSH1 register.
*/ staticvoid omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
{ if (threshold && threshold <= mcbsp->max_rx_thres)
MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
}
/* * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
*/ static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp)
{
u16 buffstat;
/* Returns the number of free locations in the buffer */
buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
/* Number of slots are different in McBSP ports */ return mcbsp->pdata->buffer_size - buffstat;
}
/* * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO * to reach the threshold value (when the DMA will be triggered to read it)
*/ static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp)
{
u16 buffstat, threshold;
/* Returns the number of used locations in the buffer */
buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); /* RX threshold */
threshold = MCBSP_READ(mcbsp, THRSH1);
/* Return the number of location till we reach the threshold limit */ if (threshold <= buffstat) return 0; else return threshold - buffstat;
}
staticint omap_mcbsp_request(struct omap_mcbsp *mcbsp)
{ void *reg_cache; int err;
reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); if (!reg_cache) return -ENOMEM;
spin_lock(&mcbsp->lock); if (!mcbsp->free) {
dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id);
err = -EBUSY; goto err_kfree;
}
/* * Make sure that transmitter, receiver and sample-rate generator are * not running before activating IRQs.
*/
MCBSP_WRITE(mcbsp, SPCR1, 0);
MCBSP_WRITE(mcbsp, SPCR2, 0);
if (mcbsp->irq) {
err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, "McBSP", (void *)mcbsp); if (err != 0) {
dev_err(mcbsp->dev, "Unable to request IRQ\n"); goto err_clk_disable;
}
} else {
err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, "McBSP TX", (void *)mcbsp); if (err != 0) {
dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); goto err_clk_disable;
}
/* * Select CLKS source from internal source unconditionally before * marking the McBSP port as free. * If the external clock source via MCBSP_CLKS pin has been selected the * system will refuse to enter idle if the CLKS pin source is not reset * back to internal source.
*/ if (!mcbsp_omap1())
omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC);
spin_lock(&mcbsp->lock); if (mcbsp->free)
dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); else
mcbsp->free = true;
mcbsp->reg_cache = NULL;
spin_unlock(&mcbsp->lock);
kfree(reg_cache);
}
/* * Here we start the McBSP, by enabling transmitter, receiver or both. * If no transmitter or receiver is active prior calling, then sample-rate * generator and frame sync are started.
*/ staticvoid omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream)
{ int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); int rx = !tx; int enable_srg = 0;
u16 w;
if (mcbsp->st_data)
omap_mcbsp_st_start(mcbsp);
/* Only enable SRG, if McBSP is master */
w = MCBSP_READ_CACHE(mcbsp, PCR0); if (w & (FSXM | FSRM | CLKXM | CLKRM))
enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
if (enable_srg) { /* Start the sample generator */
w = MCBSP_READ_CACHE(mcbsp, SPCR2);
MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
}
/* Enable transmitter and receiver */
tx &= 1;
w = MCBSP_READ_CACHE(mcbsp, SPCR2);
MCBSP_WRITE(mcbsp, SPCR2, w | tx);
rx &= 1;
w = MCBSP_READ_CACHE(mcbsp, SPCR1);
MCBSP_WRITE(mcbsp, SPCR1, w | rx);
/* * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec * REVISIT: 100us may give enough time for two CLKSRG, however * due to some unknown PM related, clock gating etc. reason it * is now at 500us.
*/
udelay(500);
if (enable_srg) { /* Start frame sync */
w = MCBSP_READ_CACHE(mcbsp, SPCR2);
MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
}
if (mcbsp->pdata->has_ccr) { /* Release the transmitter and receiver */
w = MCBSP_READ_CACHE(mcbsp, XCCR);
w &= ~(tx ? XDISABLE : 0);
MCBSP_WRITE(mcbsp, XCCR, w);
w = MCBSP_READ_CACHE(mcbsp, RCCR);
w &= ~(rx ? RDISABLE : 0);
MCBSP_WRITE(mcbsp, RCCR, w);
}
static ssize_t dma_op_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); int dma_op_mode, i = 0;
ssize_t len = 0; constchar * const *s;
dma_op_mode = mcbsp->dma_op_mode;
for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { if (dma_op_mode == i)
len += sysfs_emit_at(buf, len, "[%s] ", *s); else
len += sysfs_emit_at(buf, len, "%s ", *s);
}
len += sysfs_emit_at(buf, len, "\n");
/* * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. * 730 has only 2 McBSP, and both of them are MPU peripherals.
*/ staticint omap_mcbsp_init(struct platform_device *pdev)
{ struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); struct resource *res; int ret;
spin_lock_init(&mcbsp->lock);
mcbsp->free = true;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); if (!res)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(mcbsp->io_base)) return PTR_ERR(mcbsp->io_base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); if (!res)
mcbsp->phys_dma_base = mcbsp->phys_base; else
mcbsp->phys_dma_base = res->start;
/* * OMAP1, 2 uses two interrupt lines: TX, RX * OMAP2430, OMAP3 SoC have combined IRQ line as well. * OMAP4 and newer SoC only have the combined IRQ line. * Use the combined IRQ if available since it gives better debugging * possibilities.
*/
mcbsp->irq = platform_get_irq_byname(pdev, "common"); if (mcbsp->irq == -ENXIO) {
mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
mcbsp->fclk = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(mcbsp->fclk)) {
ret = PTR_ERR(mcbsp->fclk);
dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); return ret;
}
mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; if (mcbsp->pdata->buffer_size) { /* * Initially configure the maximum thresholds to a safe value. * The McBSP FIFO usage with these values should not go under * 16 locations. * If the whole FIFO without safety buffer is used, than there * is a possibility that the DMA will be not able to push the * new data on time, causing channel shifts in runtime.
*/
mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
ret = devm_device_add_group(mcbsp->dev, &additional_attr_group); if (ret) {
dev_err(mcbsp->dev, "Unable to create additional controls\n"); return ret;
}
}
return omap_mcbsp_st_init(pdev);
}
/* * Stream DMA parameters. DMA request line and port address are set runtime * since they are different between OMAP1 and later OMAPs
*/ staticvoid omap_mcbsp_set_threshold(struct snd_pcm_substream *substream, unsignedint packet_size)
{ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); int words;
/* No need to proceed further if McBSP does not have FIFO */ if (mcbsp->pdata->buffer_size == 0) return;
/* * Configure McBSP threshold based on either: * packet_size, when the sDMA is in packet mode, or based on the * period size in THRESHOLD mode, otherwise use McBSP threshold = 1 * for mono streams.
*/ if (packet_size)
words = packet_size; else
words = 1;
if (!snd_soc_dai_active(cpu_dai))
err = omap_mcbsp_request(mcbsp);
/* * OMAP3 McBSP FIFO is word structured. * McBSP2 has 1024 + 256 = 1280 word long buffer, * McBSP1,3,4,5 has 128 word long buffer * This means that the size of the FIFO depends on the sample format. * For example on McBSP3: * 16bit samples: size is 128 * 2 = 256 bytes * 32bit samples: size is 128 * 4 = 512 bytes * It is simpler to place constraint for buffer and period based on * channels. * McBSP3 as example again (16 or 32 bit samples): * 1 channel (mono): size is 128 frames (128 words) * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
*/ if (mcbsp->pdata->buffer_size) { /* * Rule for the buffer size. We should not allow * smaller buffer than the FIFO size to avoid underruns. * This applies only for the playback stream.
*/ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
omap_mcbsp_hwrule_min_buffersize,
mcbsp,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
/* Make sure, that the period size is always even */
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
}
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
mcbsp->active++;
omap_mcbsp_start(mcbsp, substream->stream); break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
omap_mcbsp_stop(mcbsp, substream->stream);
mcbsp->active--; break; default: return -EINVAL;
}
/* * Divide the used locations with the channel count to get the * FIFO usage in samples (don't care about partial samples in the * buffer).
*/
delay = fifo_use / substream->runtime->channels;
switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE:
wlen = 16; break; case SNDRV_PCM_FORMAT_S32_LE:
wlen = 32; break; default: return -EINVAL;
} if (buffer_size) { int latency;
if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { int period_words, max_thrsh; int divider = 0;
period_words = params_period_bytes(params) / (wlen / 8); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
max_thrsh = mcbsp->max_tx_thres; else
max_thrsh = mcbsp->max_rx_thres; /* * Use sDMA packet mode if McBSP is in threshold mode: * If period words less than the FIFO size the packet * size is set to the number of period words, otherwise * Look for the biggest threshold value which divides * the period size evenly.
*/
divider = period_words / max_thrsh; if (period_words % max_thrsh)
divider++; while (period_words % divider &&
divider < period_words)
divider++; if (divider == period_words) return -EINVAL;
pkt_size = period_words / divider;
} elseif (channels > 1) { /* Use packet mode for non mono streams */
pkt_size = channels;
}
switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: /* Set word lengths */
regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16);
regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16);
regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16);
regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16); break; case SNDRV_PCM_FORMAT_S32_LE: /* Set word lengths */
regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32);
regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32);
regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32);
regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32); break; default: /* Unsupported PCM format */ return -EINVAL;
}
/* In McBSP master modes, FRAME (i.e. sample rate) is generated
* by _counting_ BCLKs. Calculate frame size in BCLKs */
master = mcbsp->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; if (master == SND_SOC_DAIFMT_BP_FP) {
div = mcbsp->clk_div ? mcbsp->clk_div : 1;
framesize = (mcbsp->in_freq / div) / params_rate(params);
if (framesize < wlen * channels) {
printk(KERN_ERR "%s: not enough bandwidth for desired rate and " "channels\n", __func__); return -EINVAL;
}
} else
framesize = wlen * channels;
/* Set FS period and length in terms of bit clock periods */
regs->srgr2 &= ~FPER(0xfff);
regs->srgr1 &= ~FWID(0xff); switch (format) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_LEFT_J:
regs->srgr2 |= FPER(framesize - 1);
regs->srgr1 |= FWID((framesize >> 1) - 1); break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B:
regs->srgr2 |= FPER(framesize - 1);
regs->srgr1 |= FWID(0); break;
}
/* * This must be called before _set_clkdiv and _set_sysclk since McBSP register * cache is initialized here
*/ staticint omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsignedint fmt)
{ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; bool inv_fs = false;
if (mcbsp->configured) return 0;
mcbsp->fmt = fmt;
memset(regs, 0, sizeof(*regs)); /* Generic McBSP register settings */
regs->spcr2 |= XINTM(3) | FREE;
regs->spcr1 |= RINTM(3); /* RFIG and XFIG are not defined in 2430 and on OMAP3+ */ if (!mcbsp->pdata->has_ccr) {
regs->rcr2 |= RFIG;
regs->xcr2 |= XFIG;
}
/* Configure XCCR/RCCR only for revisions which have ccr registers */ if (mcbsp->pdata->has_ccr) {
regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* 1-bit data delay */
regs->rcr2 |= RDATDLY(1);
regs->xcr2 |= XDATDLY(1); break; case SND_SOC_DAIFMT_LEFT_J: /* 0-bit data delay */
regs->rcr2 |= RDATDLY(0);
regs->xcr2 |= XDATDLY(0);
regs->spcr1 |= RJUST(2); /* Invert FS polarity configuration */
inv_fs = true; break; case SND_SOC_DAIFMT_DSP_A: /* 1-bit data delay */
regs->rcr2 |= RDATDLY(1);
regs->xcr2 |= XDATDLY(1); /* Invert FS polarity configuration */
inv_fs = true; break; case SND_SOC_DAIFMT_DSP_B: /* 0-bit data delay */
regs->rcr2 |= RDATDLY(0);
regs->xcr2 |= XDATDLY(0); /* Invert FS polarity configuration */
inv_fs = true; break; default: /* Unsupported data format */ return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BP_FP: /* McBSP master. Set FS and bit clocks as outputs */
regs->pcr0 |= FSXM | FSRM |
CLKXM | CLKRM; /* Sample rate generator drives the FS */
regs->srgr2 |= FSGM; break; case SND_SOC_DAIFMT_BC_FP: /* McBSP slave. FS clock as output */
regs->srgr2 |= FSGM;
regs->pcr0 |= FSXM | FSRM; break; case SND_SOC_DAIFMT_BC_FC: /* McBSP slave */ break; default: /* Unsupported master/slave configuration */ return -EINVAL;
}
/* Set bit clock (CLKX/CLKR) and FS polarities */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: /* * Normal BCLK + FS. * FS active low. TX data driven on falling edge of bit clock * and RX data sampled on rising edge of bit clock.
*/
regs->pcr0 |= FSXP | FSRP |
CLKXP | CLKRP; break; case SND_SOC_DAIFMT_NB_IF:
regs->pcr0 |= CLKXP | CLKRP; break; case SND_SOC_DAIFMT_IB_NF:
regs->pcr0 |= FSXP | FSRP; break; case SND_SOC_DAIFMT_IB_IF: break; default: return -EINVAL;
} if (inv_fs)
regs->pcr0 ^= FSXP | FSRP;
return 0;
}
staticint omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div)
{ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
switch (clk_id) { case OMAP_MCBSP_SYSCLK_CLK:
regs->srgr2 |= CLKSM; break; case OMAP_MCBSP_SYSCLK_CLKS_FCLK: if (mcbsp_omap1()) {
err = -EINVAL; break;
}
err = omap2_mcbsp_set_clks_src(mcbsp,
MCBSP_CLKS_PRCM_SRC); break; case OMAP_MCBSP_SYSCLK_CLKS_EXT: if (mcbsp_omap1()) {
err = 0; break;
}
err = omap2_mcbsp_set_clks_src(mcbsp,
MCBSP_CLKS_PAD_SRC); break;
case OMAP_MCBSP_SYSCLK_CLKX_EXT:
regs->srgr2 |= CLKSM;
regs->pcr0 |= SCLKME; /* * If McBSP is master but yet the CLKX/CLKR pin drives the SRG, * disable output on those pins. This enables to inject the * reference clock through CLKX/CLKR. For this to work * set_dai_sysclk() _needs_ to be called after set_dai_fmt().
*/
regs->pcr0 &= ~CLKXM; break; case OMAP_MCBSP_SYSCLK_CLKR_EXT:
regs->pcr0 |= SCLKME; /* Disable ouput on CLKR pin in master mode */
regs->pcr0 &= ~CLKRM; break; default:
err = -ENODEV;
}
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.