/* Mask for R5F register. Set all relevant interrupt for playback handler */ #define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \
BIT(R5F_ESR1_SHIFT) | \
BIT(R5F_ESR3_SHIFT))
/* Mask for R5F register. Set all relevant interrupt for capture handler */ #define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
/* * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick. * This number should be a multiple of 256. Minimum value is 256
*/ #define PERIOD_BYTES_MIN 0x100
/* A period is basically an interrupt */
.period_bytes_min = PERIOD_BYTES_MIN,
.period_bytes_max = 0x10000,
/* period_min/max gives range of approx interrupts per buffer */
.periods_min = 2,
.periods_max = 8,
/* * maximum buffer size in bytes = period_bytes_max * periods_max * We allocate this amount of data for each enabled channel
*/
.buffer_bytes_max = 4 * 0x8000,
};
if (is_playback) { /* Set the pointers to indicate full (flip uppermost bit) */
initial_rd = start;
initial_wr = initial_rd ^ BIT(31);
} else { /* Set the pointers to indicate empty */
initial_wr = start;
initial_rd = initial_wr;
}
end = start + bufsize - 1;
/* * The interrupt will fire when free/full mark is *exceeded* * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark * to be PERIOD_BYTES_MIN less than the period size.
*/
fmark_val = periodsize - PERIOD_BYTES_MIN;
/* * If free/full mark interrupt occurs, provide timestamp * to ALSA and update appropriate idx by period_bytes
*/
snd_pcm_period_elapsed(substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* Set the ring buffer to full */
regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
regval = regval ^ BIT(31);
writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
} else { /* Set the ring buffer to empty */
regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
}
}
/* * ESR0/1/3 status Description * 0x1 I2S0_out port caused interrupt * 0x2 I2S1_out port caused interrupt * 0x4 I2S2_out port caused interrupt * 0x8 SPDIF_out port caused interrupt
*/ staticvoid handle_playback_irq(struct cygnus_audio *cygaud)
{ void __iomem *audio_io;
u32 port;
u32 esr_status0, esr_status1, esr_status3;
audio_io = cygaud->audio;
/* * ESR status gets updates with/without interrupts enabled. * So, check the ESR mask, which provides interrupt enable/ * disable status and use it to determine which ESR status * should be serviced.
*/
esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
u32 esrmask = BIT(port);
/* * Ringbuffer or FIFO underflow * If we get this interrupt then, it is also true that we have * not yet responded to the freemark interrupt. * Log a debug message. The freemark handler below will * handle getting everything going again.
*/ if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
dev_dbg(cygaud->dev, "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
esr_status0, esr_status1, esr_status3);
}
/* * Freemark is hit. This is the normal interrupt. * In typical operation the read and write regs will be equal
*/ if (esrmask & esr_status3) { struct snd_pcm_substream *playstr;
/* Clear ESR interrupt */
writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET); /* Rearm freemark logic by writing 1 to the correct bit */
writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
}
/* * ESR2/4 status Description * 0x1 I2S0_in port caused interrupt * 0x2 I2S1_in port caused interrupt * 0x4 I2S2_in port caused interrupt
*/ staticvoid handle_capture_irq(struct cygnus_audio *cygaud)
{ void __iomem *audio_io;
u32 port;
u32 esr_status2, esr_status4;
audio_io = cygaud->audio;
/* * ESR status gets updates with/without interrupts enabled. * So, check the ESR mask, which provides interrupt enable/ * disable status and use it to determine which ESR status * should be serviced.
*/
esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
u32 esrmask = BIT(port);
/* * Ringbuffer or FIFO overflow * If we get this interrupt then, it is also true that we have * not yet responded to the fullmark interrupt. * Log a debug message. The fullmark handler below will * handle getting everything going again.
*/ if (esrmask & esr_status2)
dev_dbg(cygaud->dev, "Overflow: esr2=0x%x\n", esr_status2);
if (esrmask & esr_status4) { struct snd_pcm_substream *capstr;
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN); if (ret < 0) return ret;
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN); if (ret < 0) return ret; /* * Keep track of which substream belongs to which port. * This info is needed by snd_pcm_period_elapsed() in irq_handler
*/ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
aio->play_stream = substream; else
aio->capture_stream = substream;
static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream)
{ struct cygnus_aio_port *aio; unsignedint res = 0, cur = 0, base = 0; struct ringbuf_regs *p_rbuf = NULL;
aio = cygnus_dai_get_dma_data(substream);
/* * Get the offset of the current read (for playack) or write * index (for capture). Report this value back to the asoc framework.
*/
p_rbuf = get_ringbuf(substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cur = readl(aio->cygaud->audio + p_rbuf->rdaddr); else
cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
/* * Mask off the MSB of the rdaddr,wraddr and baseaddr * since MSB is not part of the address
*/
res = (cur & 0x7fffffff) - (base & 0x7fffffff);
if (!card->dev->dma_mask)
card->dev->dma_mask = &cygnus_dma_dmamask; if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
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.