/*
* 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/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 <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.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 "ac97c.h"
/* Serialize access to opened variable */
static DEFINE_MUTEX(opened_mutex);
struct atmel_ac97c {
struct clk *pclk;
struct platform_device *pdev;
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;
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)
static struct snd_pcm_hardware atmel_ac97c_hw = {
.info = (SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
| SNDRV_PCM_INFO_INTERLEAVED
| SNDRV_PCM_INFO_BLOCK_TRANSFER
| SNDRV_PCM_INFO_JOINT_DUPLEX
| SNDRV_PCM_INFO_RESUME
| SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_BE
| SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_CONTINUOUS),
.rate_min = 4000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 2 * 2 * 64 * 2048,
.period_bytes_min = 4096,
.period_bytes_max = 4096,
.periods_min = 6,
.periods_max = 64,
};
static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
mutex_lock(&opened_mutex);
chip->opened++;
runtime->hw = atmel_ac97c_hw;
if (chip->cur_rate) {
runtime->hw.rate_min = chip->cur_rate;
runtime->hw.rate_max = chip->cur_rate;
}
if (chip->cur_format)
runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
mutex_unlock(&opened_mutex);
chip->playback_substream = substream;
return 0;
}
static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
mutex_lock(&opened_mutex);
chip->opened++;
runtime->hw = atmel_ac97c_hw;
if (chip->cur_rate) {
runtime->hw.rate_min = chip->cur_rate;
runtime->hw.rate_max = chip->cur_rate;
}
if (chip->cur_format)
runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
mutex_unlock(&opened_mutex);
chip->capture_substream = substream;
return 0;
}
static int atmel_ac97c_playback_close(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
mutex_lock(&opened_mutex);
chip->opened--;
if (!chip->opened) {
chip->cur_rate = 0;
chip->cur_format = 0;
}
mutex_unlock(&opened_mutex);
chip->playback_substream = NULL;
return 0;
}
static int atmel_ac97c_capture_close(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
mutex_lock(&opened_mutex);
chip->opened--;
if (!chip->opened) {
chip->cur_rate = 0;
chip->cur_format = 0;
}
mutex_unlock(&opened_mutex);
chip->capture_substream = NULL;
return 0;
}
static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct atmel_ac97c *chip