openwrt_archive/target/linux/ar71xx/files/sound/soc/ath79/ath79-mbox.c

247 lines
7.0 KiB
C

/*
* ath79-mbox.c -- ALSA MBOX DMA management functions
*
* Copyright (c) 2013 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/dmapool.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#include <asm/mach-ath79/ath79.h>
#include "ath79-pcm.h"
#include "ath79-i2s.h"
spinlock_t ath79_pcm_lock;
static struct dma_pool *ath79_pcm_cache;
void ath79_mbox_reset(void)
{
u32 t;
spin_lock(&ath79_pcm_lock);
t = ath79_reset_rr(AR934X_RESET_REG_RESET_MODULE);
t |= AR934X_RESET_MBOX;
ath79_reset_wr(AR934X_RESET_REG_RESET_MODULE, t);
udelay(50);
t &= ~(AR934X_RESET_MBOX);
ath79_reset_wr(AR934X_RESET_REG_RESET_MODULE, t);
spin_unlock(&ath79_pcm_lock);
}
void ath79_mbox_fifo_reset(u32 mask)
{
ath79_dma_wr(AR934X_DMA_REG_MBOX_FIFO_RESET, mask);
udelay(50);
/* Datasheet says we should reset the stereo controller whenever
* we reset the MBOX DMA controller */
ath79_stereo_reset();
}
void ath79_mbox_interrupt_enable(u32 mask)
{
u32 t;
spin_lock(&ath79_pcm_lock);
t = ath79_dma_rr(AR934X_DMA_REG_MBOX_INT_ENABLE);
t |= mask;
ath79_dma_wr(AR934X_DMA_REG_MBOX_INT_ENABLE, t);
spin_unlock(&ath79_pcm_lock);
}
void ath79_mbox_interrupt_ack(u32 mask)
{
ath79_dma_wr(AR934X_DMA_REG_MBOX_INT_STATUS, mask);
ath79_reset_wr(AR71XX_RESET_REG_MISC_INT_STATUS, ~(MISC_INT_DMA));
/* Flush these two registers */
ath79_dma_rr(AR934X_DMA_REG_MBOX_INT_STATUS);
ath79_reset_rr(AR71XX_RESET_REG_MISC_INT_STATUS);
}
void ath79_mbox_dma_start(struct ath79_pcm_rt_priv *rtpriv)
{
if (rtpriv->direction == SNDRV_PCM_STREAM_PLAYBACK) {
ath79_dma_wr(AR934X_DMA_REG_MBOX0_DMA_RX_CONTROL,
AR934X_DMA_MBOX_DMA_CONTROL_START);
ath79_dma_rr(AR934X_DMA_REG_MBOX0_DMA_RX_CONTROL);
} else {
ath79_dma_wr(AR934X_DMA_REG_MBOX0_DMA_TX_CONTROL,
AR934X_DMA_MBOX_DMA_CONTROL_START);
ath79_dma_rr(AR934X_DMA_REG_MBOX0_DMA_TX_CONTROL);
}
}
void ath79_mbox_dma_stop(struct ath79_pcm_rt_priv *rtpriv)
{
if (rtpriv->direction == SNDRV_PCM_STREAM_PLAYBACK) {
ath79_dma_wr(AR934X_DMA_REG_MBOX0_DMA_RX_CONTROL,
AR934X_DMA_MBOX_DMA_CONTROL_STOP);
ath79_dma_rr(AR934X_DMA_REG_MBOX0_DMA_RX_CONTROL);
} else {
ath79_dma_wr(AR934X_DMA_REG_MBOX0_DMA_TX_CONTROL,
AR934X_DMA_MBOX_DMA_CONTROL_STOP);
ath79_dma_rr(AR934X_DMA_REG_MBOX0_DMA_TX_CONTROL);
}
/* Delay for the dynamically calculated max time based on
sample size, channel, sample rate + margin to ensure that the
DMA engine will be truly idle. */
mdelay(rtpriv->delay_time);
}
void ath79_mbox_dma_reset(void)
{
ath79_mbox_reset();
ath79_mbox_fifo_reset(AR934X_DMA_MBOX0_FIFO_RESET_RX |
AR934X_DMA_MBOX0_FIFO_RESET_TX);
}
void ath79_mbox_dma_prepare(struct ath79_pcm_rt_priv *rtpriv)
{
struct ath79_pcm_desc *desc;
u32 t;
if (rtpriv->direction == SNDRV_PCM_STREAM_PLAYBACK) {
/* Request the DMA channel to the controller */
t = ath79_dma_rr(AR934X_DMA_REG_MBOX_DMA_POLICY);
ath79_dma_wr(AR934X_DMA_REG_MBOX_DMA_POLICY,
t | AR934X_DMA_MBOX_DMA_POLICY_RX_QUANTUM |
(6 << AR934X_DMA_MBOX_DMA_POLICY_TX_FIFO_THRESH_SHIFT));
/* The direction is indicated from the DMA engine perspective
* i.e. we'll be using the RX registers for Playback and
* the TX registers for capture */
desc = list_first_entry(&rtpriv->dma_head, struct ath79_pcm_desc, list);
ath79_dma_wr(AR934X_DMA_REG_MBOX0_DMA_RX_DESCRIPTOR_BASE,
(u32) desc->phys);
ath79_mbox_interrupt_enable(AR934X_DMA_MBOX0_INT_RX_COMPLETE);
} else {
/* Request the DMA channel to the controller */
t = ath79_dma_rr(AR934X_DMA_REG_MBOX_DMA_POLICY);
ath79_dma_wr(AR934X_DMA_REG_MBOX_DMA_POLICY,
t | AR934X_DMA_MBOX_DMA_POLICY_TX_QUANTUM |
(6 << AR934X_DMA_MBOX_DMA_POLICY_TX_FIFO_THRESH_SHIFT));
desc = list_first_entry(&rtpriv->dma_head, struct ath79_pcm_desc, list);
ath79_dma_wr(AR934X_DMA_REG_MBOX0_DMA_TX_DESCRIPTOR_BASE,
(u32) desc->phys);
ath79_mbox_interrupt_enable(AR934X_DMA_MBOX0_INT_TX_COMPLETE);
}
}
int ath79_mbox_dma_map(struct ath79_pcm_rt_priv *rtpriv, dma_addr_t baseaddr,
int period_bytes,int bufsize)
{
struct list_head *head = &rtpriv->dma_head;
struct ath79_pcm_desc *desc, *prev;
dma_addr_t desc_p;
unsigned int offset = 0;
spin_lock(&ath79_pcm_lock);
rtpriv->elapsed_size = 0;
/* We loop until we have enough buffers to map the requested DMA area */
do {
/* Allocate a descriptor and insert it into the DMA ring */
desc = dma_pool_alloc(ath79_pcm_cache, GFP_KERNEL, &desc_p);
if(!desc) {
return -ENOMEM;
}
memset(desc, 0, sizeof(struct ath79_pcm_desc));
desc->phys = desc_p;
list_add_tail(&desc->list, head);
desc->OWN = 1;
desc->rsvd1 = desc->rsvd2 = desc->rsvd3 = desc->EOM = 0;
/* buffer size may not be a multiple of period_bytes */
if (bufsize >= offset + period_bytes) {
desc->size = period_bytes;
} else {
desc->size = bufsize - offset;
}
desc->BufPtr = baseaddr + offset;
/* For now, we assume the buffer is always full
* -->length == size */
desc->length = desc->size;
/* We need to make sure we are not the first descriptor.
* If we are, prev doesn't point to a struct ath79_pcm_desc */
if (desc->list.prev != head) {
prev =
list_entry(desc->list.prev, struct ath79_pcm_desc,
list);
prev->NextPtr = desc->phys;
}
offset += desc->size;
} while (offset < bufsize);
/* Once all the descriptors have been created, we can close the loop
* by pointing from the last one to the first one */
desc = list_first_entry(head, struct ath79_pcm_desc, list);
prev = list_entry(head->prev, struct ath79_pcm_desc, list);
prev->NextPtr = desc->phys;
spin_unlock(&ath79_pcm_lock);
return 0;
}
void ath79_mbox_dma_unmap(struct ath79_pcm_rt_priv *rtpriv)
{
struct list_head *head = &rtpriv->dma_head;
struct ath79_pcm_desc *desc, *n;
spin_lock(&ath79_pcm_lock);
list_for_each_entry_safe(desc, n, head, list) {
list_del(&desc->list);
dma_pool_free(ath79_pcm_cache, desc, desc->phys);
}
spin_unlock(&ath79_pcm_lock);
return;
}
int ath79_mbox_dma_init(struct device *dev)
{
int ret = 0;
/* Allocate a DMA pool to store the MBOX descriptor */
ath79_pcm_cache = dma_pool_create("ath79_pcm_pool", dev,
sizeof(struct ath79_pcm_desc), 4, 0);
if (!ath79_pcm_cache)
ret = -ENOMEM;
return ret;
}
void ath79_mbox_dma_exit(void)
{
dma_pool_destroy(ath79_pcm_cache);
ath79_pcm_cache = NULL;
}