/*
* Driver for Atmel AC97C
*
* Copyright (C) 2005-2009 Atmel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/bitmap.h>
#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/types.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/ac97_codec.h>
#include <sound/atmel-ac97c.h>
#include <sound/memalloc.h>
#include <linux/dw_dmac.h>
#include <mach/cpu.h>
#ifdef CONFIG_ARCH_AT91
#include <mach/hardware.h>
#endif
#include "ac97c.h"
enum {
DMA_TX_READY = 0,
DMA_RX_READY,
DMA_TX_CHAN_PRESENT,
DMA_RX_CHAN_PRESENT,
};
/* Serialize access to opened variable */
static DEFINE_MUTEX(opened_mutex);
struct atmel_ac97c_dma {
struct dma_chan *rx_chan;
struct dma_chan *tx_chan;
};
struct atmel_ac97c {
struct clk *pclk;
struct platform_device *pdev;
struct atmel_ac97c_dma dma;
struct snd_pcm_substream *playback_substream;
struct snd_pcm_substream *capture_substream;
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_ac97 *ac97;
struct snd_ac97_bus *ac97_bus;
u64 cur_format;
unsigned int cur_rate;
unsigned long flags;
int playback_period, capture_period;
/* Serialize access to opened variable */
spinlock_t lock;
void __iomem *regs;
int irq;
int opened;
int reset_pin;
};
#define get_chip(card) ((struct atmel_ac97c *)(card)->private_data)
#define ac97c_writel(chip, reg, val) \
__raw_writel((val), (chip)->regs + AC97C_##reg)
#define ac97c_readl(chip, reg) \
__raw_readl((chip)->regs + AC97C_##reg)
/* This function is called by the DMA driver. */
static void atmel_ac97c_dma_playback_period_done(void *arg)
{
struct atmel_ac97c *chip = arg;
snd_pcm_period_elapsed(chip->playback_substream);
}
static void atmel_ac97c_dma_capture_period_done(void *arg)
{
struct atmel_ac97c *chip = arg;
snd_pcm_period_elapsed(chip->capture_substream);
}
static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip,
struct snd_pcm_substream *substream,
enum dma_transfer_direction direction)
{
struct dma_chan *chan;
struct dw_cyclic_desc *cdesc;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long buffer_len, period_len;
/*
* We don't do DMA on "complex" transfers, i.e. with
* non-halfword-aligned buffers or lengths.
*/
if (runtime->dma_addr & 1 || runtime->buffer_size & 1) {
dev_dbg(&chip->pdev->dev, "too complex transfer\n");
return -EINVAL;
}
if (direction == DMA_MEM_TO_DEV)
chan = chip->dma.tx_chan;
else
chan = chip->dma.rx_chan;
buffer_len = frames_to_bytes(runtime, runtime->buffer_size);