Commit 1a3c5a49 authored by Timur Tabi's avatar Timur Tabi Committed by Liam Girdwood

asoc/multi-component: fsl: add support for 36-bit physical addresses

Update the DMA driver used by the Freescale MPC8610 HPCD audio driver to
support 36-bit physical addresses, for both DMA buffers and the SSI registers.

The DMA driver calls snd_dma_alloc_pages() to allocate the DMA buffers for
playback and capture.  This function is just a front-end for
dma_alloc_coherent().  Currently, dma_alloc_coherent() only allocates buffers
in low memory (it ignores GFP_HIGHMEM), so we never actually get a DMA buffer
with a real 36-bit physical address.
Signed-off-by: default avatarTimur Tabi <timur@freescale.com>
Signed-off-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
parent 6e6f6622
...@@ -175,13 +175,23 @@ static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private) ...@@ -175,13 +175,23 @@ static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private)
struct fsl_dma_link_descriptor *link = struct fsl_dma_link_descriptor *link =
&dma_private->link[dma_private->current_link]; &dma_private->link[dma_private->current_link];
/* Update our link descriptors to point to the next period */ /* Update our link descriptors to point to the next period. On a 36-bit
if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) * system, we also need to update the ESAD bits. We also set (keep) the
link->source_addr = * snoop bits. See the comments in fsl_dma_hw_params() about snooping.
cpu_to_be32(dma_private->dma_buf_next); */
else if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
link->dest_addr = link->source_addr = cpu_to_be32(dma_private->dma_buf_next);
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 */ /* Update our variables for next time */
dma_private->dma_buf_next += dma_private->period_size; dma_private->dma_buf_next += dma_private->period_size;
...@@ -273,11 +283,19 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) ...@@ -273,11 +283,19 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
* This function is called when the codec driver calls snd_soc_new_pcms(), * 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 * once for each .dai_link in the machine driver's snd_soc_card
* structure. * 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.
*/ */
static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm) struct snd_pcm *pcm)
{ {
static u64 fsl_dma_dmamask = DMA_BIT_MASK(32); static u64 fsl_dma_dmamask = DMA_BIT_MASK(36);
int ret; int ret;
if (!card->dev->dma_mask) if (!card->dev->dma_mask)
...@@ -609,12 +627,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, ...@@ -609,12 +627,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
link->count = cpu_to_be32(period_size); link->count = cpu_to_be32(period_size);
/* Even though the DMA controller supports 36-bit addressing, /* The snoop bit tells the DMA controller whether it should tell
* for simplicity we allow only 32-bit addresses for the audio
* buffer itself. This was enforced in fsl_dma_new() with the
* DMA mask.
*
* The snoop bit tells the DMA controller whether it should tell
* the ECM to snoop during a read or write to an address. For * 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 * audio, we use DMA to transfer data between memory and an I/O
* device (the SSI's STX0 or SRX0 register). Snooping is only * device (the SSI's STX0 or SRX0 register). Snooping is only
...@@ -629,20 +642,24 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, ...@@ -629,20 +642,24 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
* flush out the data for the previous period. So if you * flush out the data for the previous period. So if you
* increased period_bytes_min to a large enough size, you might * increased period_bytes_min to a large enough size, you might
* get more performance by not snooping, and you'll still be * get more performance by not snooping, and you'll still be
* okay. * okay. You'll need to update fsl_dma_update_pointers() also.
*/ */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
link->source_addr = cpu_to_be32(temp_addr); link->source_addr = cpu_to_be32(temp_addr);
link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP |
upper_32_bits(temp_addr));
link->dest_addr = cpu_to_be32(ssi_sxx_phys); link->dest_addr = cpu_to_be32(ssi_sxx_phys);
link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP); link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP |
upper_32_bits(ssi_sxx_phys));
} else { } else {
link->source_addr = cpu_to_be32(ssi_sxx_phys); link->source_addr = cpu_to_be32(ssi_sxx_phys);
link->source_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP); link->source_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP |
upper_32_bits(ssi_sxx_phys));
link->dest_addr = cpu_to_be32(temp_addr); link->dest_addr = cpu_to_be32(temp_addr);
link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP |
upper_32_bits(temp_addr));
} }
temp_addr += period_size; temp_addr += period_size;
...@@ -673,10 +690,23 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) ...@@ -673,10 +690,23 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
dma_addr_t position; dma_addr_t position;
snd_pcm_uframes_t frames; snd_pcm_uframes_t frames;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) /* 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); position = in_be32(&dma_channel->sar);
else #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); 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. * When capture is started, the SSI immediately starts to fill its FIFO.
...@@ -936,11 +966,6 @@ static void __exit fsl_soc_dma_exit(void) ...@@ -936,11 +966,6 @@ static void __exit fsl_soc_dma_exit(void)
of_unregister_platform_driver(&fsl_soc_dma_driver); of_unregister_platform_driver(&fsl_soc_dma_driver);
} }
/* We want the DMA driver to be initialized before the SSI driver, so that
* when the SSI driver calls fsl_soc_dma_dai_from_node(), the DMA driver
* will already have been probed. The easiest way to do that is to make the
* __init function called via arch_initcall().
*/
module_init(fsl_soc_dma_init); module_init(fsl_soc_dma_init);
module_exit(fsl_soc_dma_exit); module_exit(fsl_soc_dma_exit);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment