// SPDX-License-Identifier: GPL-2.0 // // Freescale DMA ALSA SoC PCM driver // // Author: Timur Tabi <timur@freescale.com> // // Copyright 2007-2010 Freescale Semiconductor, Inc. // // This driver implements ASoC support for the Elo DMA controller, which is // the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms, // the PCM driver is what handles the DMA buffer.
/* * The number of DMA links to use. Two is the bare minimum, but if you * have really small links you might need more.
*/ #define NUM_DMA_LINKS 2
/** fsl_dma_private: p-substream DMA data * * Each substream has a 1-to-1 association with a DMA channel. * * The link[] array is first because it needs to be aligned on a 32-byte * boundary, so putting it first will ensure alignment without padding the * structure. * * @link[]: array of link descriptors * @dma_channel: pointer to the DMA channel's registers * @irq: IRQ for this DMA channel * @substream: pointer to the substream object, needed by the ISR * @ssi_sxx_phys: bus address of the STX or SRX register to use * @ld_buf_phys: physical address of the LD buffer * @current_link: index into link[] of the link currently being processed * @dma_buf_phys: physical address of the DMA buffer * @dma_buf_next: physical address of the next period to process * @dma_buf_end: physical address of the byte after the end of the DMA * @buffer period_size: the size of a single period * @num_periods: the number of periods in the DMA buffer
*/ struct fsl_dma_private { struct fsl_dma_link_descriptor link[NUM_DMA_LINKS]; struct ccsr_dma_channel __iomem *dma_channel; unsignedint irq; struct snd_pcm_substream *substream;
dma_addr_t ssi_sxx_phys; unsignedint ssi_fifo_depth;
dma_addr_t ld_buf_phys; unsignedint current_link;
dma_addr_t dma_buf_phys;
dma_addr_t dma_buf_next;
dma_addr_t dma_buf_end;
size_t period_size; unsignedint num_periods;
};
/** * fsl_dma_hardare: define characteristics of the PCM hardware. * * The PCM hardware is the Freescale DMA controller. This structure defines * the capabilities of that hardware. * * Since the sampling rate and data format are not controlled by the DMA * controller, we specify no limits for those values. The only exception is * period_bytes_min, which is set to a reasonably low value to prevent the * DMA controller from generating too many interrupts per second. * * Since each link descriptor has a 32-bit byte count field, we set * period_bytes_max to the largest 32-bit number. We also have no maximum * number of periods. * * Note that we specify SNDRV_PCM_INFO_JOINT_DUPLEX here, but only because a * limitation in the SSI driver requires the sample rates for playback and * capture to be the same.
*/ staticconststruct snd_pcm_hardware fsl_dma_hardware = {
/** * fsl_dma_abort_stream: tell ALSA that the DMA transfer has aborted * * This function should be called by the ISR whenever the DMA controller * halts data transfer.
*/ staticvoid fsl_dma_abort_stream(struct snd_pcm_substream *substream)
{
snd_pcm_stop_xrun(substream);
}
/** * fsl_dma_update_pointers - update LD pointers to point to the next period * * As each period is completed, this function changes the link * descriptor pointers for that period to point to the next period.
*/ staticvoid fsl_dma_update_pointers(struct fsl_dma_private *dma_private)
{ struct fsl_dma_link_descriptor *link =
&dma_private->link[dma_private->current_link];
/* Update our link descriptors to point to the next period. On a 36-bit * system, we also need to update the ESAD bits. We also set (keep) the * snoop bits. See the comments in fsl_dma_hw_params() about snooping.
*/ if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
link->source_addr = cpu_to_be32(dma_private->dma_buf_next); #ifdef CONFIG_PHYS_64BIT
link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP |
upper_32_bits(dma_private->dma_buf_next)); #endif
} else {
link->dest_addr = cpu_to_be32(dma_private->dma_buf_next); #ifdef CONFIG_PHYS_64BIT
link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP |
upper_32_bits(dma_private->dma_buf_next)); #endif
}
/* Update our variables for next time */
dma_private->dma_buf_next += dma_private->period_size;
if (dma_private->dma_buf_next >= dma_private->dma_buf_end)
dma_private->dma_buf_next = dma_private->dma_buf_phys;
if (++dma_private->current_link >= NUM_DMA_LINKS)
dma_private->current_link = 0;
}
/** * fsl_dma_isr: interrupt handler for the DMA controller * * @irq: IRQ of the DMA channel * @dev_id: pointer to the dma_private structure for this DMA channel
*/ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
{ struct fsl_dma_private *dma_private = dev_id; struct snd_pcm_substream *substream = dma_private->substream; struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct device *dev = rtd->dev; struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
irqreturn_t ret = IRQ_NONE;
u32 sr, sr2 = 0;
/* We got an interrupt, so read the status register to see what we were interrupted for.
*/
sr = in_be32(&dma_channel->sr);
if (sr & CCSR_DMA_SR_TE) {
dev_err(dev, "dma transmit error\n");
fsl_dma_abort_stream(substream);
sr2 |= CCSR_DMA_SR_TE;
ret = IRQ_HANDLED;
}
if (sr & CCSR_DMA_SR_CH)
ret = IRQ_HANDLED;
if (sr & CCSR_DMA_SR_PE) {
dev_err(dev, "dma programming error\n");
fsl_dma_abort_stream(substream);
sr2 |= CCSR_DMA_SR_PE;
ret = IRQ_HANDLED;
}
if (sr & CCSR_DMA_SR_EOLNI) {
sr2 |= CCSR_DMA_SR_EOLNI;
ret = IRQ_HANDLED;
}
if (sr & CCSR_DMA_SR_CB)
ret = IRQ_HANDLED;
if (sr & CCSR_DMA_SR_EOSI) { /* Tell ALSA we completed a period. */
snd_pcm_period_elapsed(substream);
/* * Update our link descriptors to point to the next period. We * only need to do this if the number of periods is not equal to * the number of links.
*/ if (dma_private->num_periods != NUM_DMA_LINKS)
fsl_dma_update_pointers(dma_private);
sr2 |= CCSR_DMA_SR_EOSI;
ret = IRQ_HANDLED;
}
if (sr & CCSR_DMA_SR_EOLSI) {
sr2 |= CCSR_DMA_SR_EOLSI;
ret = IRQ_HANDLED;
}
/* Clear the bits that we set */ if (sr2)
out_be32(&dma_channel->sr, sr2);
return ret;
}
/** * fsl_dma_new: initialize this PCM driver. * * This function is called when the codec driver calls snd_soc_new_pcms(), * once for each .dai_link in the machine driver's snd_soc_card * structure. * * snd_dma_alloc_pages() is just a front-end to dma_alloc_coherent(), which * (currently) always allocates the DMA buffer in lowmem, even if GFP_HIGHMEM * is specified. Therefore, any DMA buffers we allocate will always be in low * memory, but we support for 36-bit physical addresses anyway. * * Regardless of where the memory is actually allocated, since the device can * technically DMA to any 36-bit address, we do need to set the DMA mask to 36.
*/ staticint fsl_dma_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd)
{ struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; int ret;
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(36)); if (ret) return ret;
/** * fsl_dma_open: open a new substream. * * Each substream has its own DMA buffer. * * ALSA divides the DMA buffer into N periods. We create NUM_DMA_LINKS link * descriptors that ping-pong from one period to the next. For example, if * there are six periods and two link descriptors, this is how they look * before playback starts: * * The last link descriptor * ____________ points back to the first * | | * V | * ___ ___ | * | |->| |->| * |___| |___| * | | * | | * V V * _________________________________________ * | | | | | | | The DMA buffer is * | | | | | | | divided into 6 parts * |______|______|______|______|______|______| * * and here's how they look after the first period is finished playing: * * ____________ * | | * V | * ___ ___ | * | |->| |->| * |___| |___| * | | * |______________ * | | * V V * _________________________________________ * | | | | | | | * | | | | | | | * |______|______|______|______|______|______| * * The first link descriptor now points to the third period. The DMA * controller is currently playing the second period. When it finishes, it * will jump back to the first descriptor and play the third period. * * There are four reasons we do this: * * 1. The only way to get the DMA controller to automatically restart the * transfer when it gets to the end of the buffer is to use chaining * mode. Basic direct mode doesn't offer that feature. * 2. We need to receive an interrupt at the end of every period. The DMA * controller can generate an interrupt at the end of every link transfer * (aka segment). Making each period into a DMA segment will give us the * interrupts we need. * 3. By creating only two link descriptors, regardless of the number of * periods, we do not need to reallocate the link descriptors if the * number of periods changes. * 4. All of the audio data is still stored in a single, contiguous DMA * buffer, which is what ALSA expects. We're just dividing it into * contiguous parts, and creating a link descriptor for each one.
*/ staticint fsl_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
{ struct snd_pcm_runtime *runtime = substream->runtime; struct device *dev = component->dev; struct dma_object *dma =
container_of(component->driver, struct dma_object, dai); struct fsl_dma_private *dma_private; struct ccsr_dma_channel __iomem *dma_channel;
dma_addr_t ld_buf_phys;
u64 temp_link; /* Pointer to next link descriptor */
u32 mr; int ret = 0; unsignedint i;
/* * Reject any DMA buffer whose size is not a multiple of the period * size. We need to make sure that the DMA buffer can be evenly divided * into periods.
*/
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) {
dev_err(dev, "invalid buffer size\n"); return ret;
}
for (i = 0; i < NUM_DMA_LINKS; i++) {
dma_private->link[i].next = cpu_to_be64(temp_link);
temp_link += sizeof(struct fsl_dma_link_descriptor);
} /* The last link descriptor points to the first */
dma_private->link[i - 1].next = cpu_to_be64(dma_private->ld_buf_phys);
/* Tell the DMA controller where the first link descriptor is */
out_be32(&dma_channel->clndar,
CCSR_DMA_CLNDAR_ADDR(dma_private->ld_buf_phys));
out_be32(&dma_channel->eclndar,
CCSR_DMA_ECLNDAR_ADDR(dma_private->ld_buf_phys));
/* The manual says the BCR must be clear before enabling EMP */
out_be32(&dma_channel->bcr, 0);
/* * Program the mode register for interrupts, external master control, * and source/destination hold. Also clear the Channel Abort bit.
*/
mr = in_be32(&dma_channel->mr) &
~(CCSR_DMA_MR_CA | CCSR_DMA_MR_DAHE | CCSR_DMA_MR_SAHE);
/* * We want External Master Start and External Master Pause enabled, * because the SSI is controlling the DMA controller. We want the DMA * controller to be set up in advance, and then we signal only the SSI * to start transferring. * * We want End-Of-Segment Interrupts enabled, because this will generate * an interrupt at the end of each segment (each link descriptor * represents one segment). Each DMA segment is the same thing as an * ALSA period, so this is how we get an interrupt at the end of every * period. * * We want Error Interrupt enabled, so that we can get an error if * the DMA controller is mis-programmed somehow.
*/
mr |= CCSR_DMA_MR_EOSIE | CCSR_DMA_MR_EIE | CCSR_DMA_MR_EMP_EN |
CCSR_DMA_MR_EMS_EN;
/* For playback, we want the destination address to be held. For
capture, set the source address to be held. */
mr |= (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
CCSR_DMA_MR_DAHE : CCSR_DMA_MR_SAHE;
out_be32(&dma_channel->mr, mr);
return 0;
}
/** * fsl_dma_hw_params: continue initializing the DMA links * * This function obtains hardware parameters about the opened stream and * programs the DMA controller accordingly. * * One drawback of big-endian is that when copying integers of different * sizes to a fixed-sized register, the address to which the integer must be * copied is dependent on the size of the integer. * * For example, if P is the address of a 32-bit register, and X is a 32-bit * integer, then X should be copied to address P. However, if X is a 16-bit * integer, then it should be copied to P+2. If X is an 8-bit register, * then it should be copied to P+3. * * So for playback of 8-bit samples, the DMA controller must transfer single * bytes from the DMA buffer to the last byte of the STX0 register, i.e. * offset by 3 bytes. For 16-bit samples, the offset is two bytes. * * For 24-bit samples, the offset is 1 byte. However, the DMA controller * does not support 3-byte copies (the DAHTS register supports only 1, 2, 4, * and 8 bytes at a time). So we do not support packed 24-bit samples. * 24-bit data must be padded to 32 bits.
*/ staticint fsl_dma_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
{ struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data; struct device *dev = component->dev;
/* Number of bits per sample */ unsignedint sample_bits =
snd_pcm_format_physical_width(params_format(hw_params));
/* Number of bytes per frame */ unsignedint sample_bytes = sample_bits / 8;
/* Bus address of SSI STX register */
dma_addr_t ssi_sxx_phys = dma_private->ssi_sxx_phys;
/* Size of the DMA buffer, in bytes */
size_t buffer_size = params_buffer_bytes(hw_params);
/* Number of bytes per period */
size_t period_size = params_period_bytes(hw_params);
/* Pointer to next period */
dma_addr_t temp_addr = substream->dma_buffer.addr;
if (dma_private->dma_buf_next >= dma_private->dma_buf_end) /* This happens if the number of periods == NUM_DMA_LINKS */
dma_private->dma_buf_next = dma_private->dma_buf_phys;
/* Due to a quirk of the SSI's STX register, the target address * for the DMA operations depends on the sample size. So we calculate * that offset here. While we're at it, also tell the DMA controller * how much data to transfer per sample.
*/ switch (sample_bits) { case 8:
mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1;
ssi_sxx_phys += 3; break; case 16:
mr |= CCSR_DMA_MR_DAHTS_2 | CCSR_DMA_MR_SAHTS_2;
ssi_sxx_phys += 2; break; case 32:
mr |= CCSR_DMA_MR_DAHTS_4 | CCSR_DMA_MR_SAHTS_4; break; default: /* We should never get here */
dev_err(dev, "unsupported sample size %u\n", sample_bits); return -EINVAL;
}
/* * BWC determines how many bytes are sent/received before the DMA * controller checks the SSI to see if it needs to stop. BWC should * always be a multiple of the frame size, so that we always transmit * whole frames. Each frame occupies two slots in the FIFO. The * parameter for CCSR_DMA_MR_BWC() is rounded down the next power of two * (MR[BWC] can only represent even powers of two). * * To simplify the process, we set BWC to the largest value that is * less than or equal to the FIFO watermark. For playback, this ensures * that we transfer the maximum amount without overrunning the FIFO. * For capture, this ensures that we transfer the maximum amount without * underrunning the FIFO. * * f = SSI FIFO depth * w = SSI watermark value (which equals f - 2) * b = DMA bandwidth count (in bytes) * s = sample size (in bytes, which equals frame_size * 2) * * For playback, we never transmit more than the transmit FIFO * watermark, otherwise we might write more data than the FIFO can hold. * The watermark is equal to the FIFO depth minus two. * * For capture, two equations must hold: * w > f - (b / s) * w >= b / s * * So, b > 2 * s, but b must also be <= s * w. To simplify, we set * b = s * w, which is equal to * (dma_private->ssi_fifo_depth - 2) * sample_bytes.
*/
mr |= CCSR_DMA_MR_BWC((dma_private->ssi_fifo_depth - 2) * sample_bytes);
out_be32(&dma_channel->mr, mr);
for (i = 0; i < NUM_DMA_LINKS; i++) { struct fsl_dma_link_descriptor *link = &dma_private->link[i];
link->count = cpu_to_be32(period_size);
/* The snoop bit tells the DMA controller whether it should tell * the ECM to snoop during a read or write to an address. For * audio, we use DMA to transfer data between memory and an I/O * device (the SSI's STX0 or SRX0 register). Snooping is only * needed if there is a cache, so we need to snoop memory * addresses only. For playback, that means we snoop the source * but not the destination. For capture, we snoop the * destination but not the source. * * Note that failing to snoop properly is unlikely to cause * cache incoherency if the period size is larger than the * size of L1 cache. This is because filling in one period will * flush out the data for the previous period. So if you * increased period_bytes_min to a large enough size, you might * get more performance by not snooping, and you'll still be * okay. You'll need to update fsl_dma_update_pointers() also.
*/ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
link->source_addr = cpu_to_be32(temp_addr);
link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP |
upper_32_bits(temp_addr));
/** * fsl_dma_pointer: determine the current position of the DMA transfer * * This function is called by ALSA when ALSA wants to know where in the * stream buffer the hardware currently is. * * For playback, the SAR register contains the physical address of the most * recent DMA transfer. For capture, the value is in the DAR register. * * The base address of the buffer is stored in the source_addr field of the * first link descriptor.
*/ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream)
{ struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data; struct device *dev = component->dev; struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
dma_addr_t position;
snd_pcm_uframes_t frames;
/* Obtain the current DMA pointer, but don't read the ESAD bits if we * only have 32-bit DMA addresses. This function is typically called * in interrupt context, so we need to optimize it.
*/ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
position = in_be32(&dma_channel->sar); #ifdef CONFIG_PHYS_64BIT
position |= (u64)(in_be32(&dma_channel->satr) &
CCSR_DMA_ATR_ESAD_MASK) << 32; #endif
} else {
position = in_be32(&dma_channel->dar); #ifdef CONFIG_PHYS_64BIT
position |= (u64)(in_be32(&dma_channel->datr) &
CCSR_DMA_ATR_ESAD_MASK) << 32; #endif
}
/* * When capture is started, the SSI immediately starts to fill its FIFO. * This means that the DMA controller is not started until the FIFO is * full. However, ALSA calls this function before that happens, when * MR.DAR is still zero. In this case, just return zero to indicate * that nothing has been received yet.
*/ if (!position) return 0;
if ((position < dma_private->dma_buf_phys) ||
(position > dma_private->dma_buf_end)) {
dev_err(dev, "dma pointer is out of range, halting stream\n"); return SNDRV_PCM_POS_XRUN;
}
frames = bytes_to_frames(runtime, position - dma_private->dma_buf_phys);
/* * If the current address is just past the end of the buffer, wrap it * around.
*/ if (frames == runtime->buffer_size)
frames = 0;
return frames;
}
/** * fsl_dma_hw_free: release resources allocated in fsl_dma_hw_params() * * Release the resources allocated in fsl_dma_hw_params() and de-program the * registers. * * This function can be called multiple times.
*/ staticint fsl_dma_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream)
{ struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data;
if (dma_private) { struct ccsr_dma_channel __iomem *dma_channel;
dma_channel = dma_private->dma_channel;
/* Stop the DMA */
out_be32(&dma_channel->mr, CCSR_DMA_MR_CA);
out_be32(&dma_channel->mr, 0);
/* Reset all the other registers */
out_be32(&dma_channel->sr, -1);
out_be32(&dma_channel->clndar, 0);
out_be32(&dma_channel->eclndar, 0);
out_be32(&dma_channel->satr, 0);
out_be32(&dma_channel->sar, 0);
out_be32(&dma_channel->datr, 0);
out_be32(&dma_channel->dar, 0);
out_be32(&dma_channel->bcr, 0);
out_be32(&dma_channel->nlndar, 0);
out_be32(&dma_channel->enlndar, 0);
}
/** * find_ssi_node -- returns the SSI node that points to its DMA channel node * * Although this DMA driver attempts to operate independently of the other * devices, it still needs to determine some information about the SSI device * that it's working with. Unfortunately, the device tree does not contain * a pointer from the DMA channel node to the SSI node -- the pointer goes the * other way. So we need to scan the device tree for SSI nodes until we find * the one that points to the given DMA channel node. It's ugly, but at least * it's contained in this one function.
*/ staticstruct device_node *find_ssi_node(struct device_node *dma_channel_np)
{ struct device_node *ssi_np, *np;
for_each_compatible_node(ssi_np, NULL, "fsl,mpc8610-ssi") { /* Check each DMA phandle to see if it points to us. We * assume that device_node pointers are a valid comparison.
*/
np = of_parse_phandle(ssi_np, "fsl,playback-dma", 0);
of_node_put(np); if (np == dma_channel_np) return ssi_np;
/* Find the SSI node that points to us. */
ssi_np = find_ssi_node(np); if (!ssi_np) {
dev_err(&pdev->dev, "cannot find parent SSI node\n"); return -ENODEV;
}
ret = of_address_to_resource(ssi_np, 0, &res); if (ret) {
dev_err(&pdev->dev, "could not determine resources for %pOF\n",
ssi_np);
of_node_put(ssi_np); return ret;
}
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.