📄 s3c24xx-iis_c.txt
字号:
/* sound/arm/s3c24xx-iis.c
*
* (c) 2004-2005 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX ALSA IIS audio driver core
*
* 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.
*/
/* Notes:
* The I2S pins that are needed (SCLK,LRCLK,SDO,SDI) are always
* configured when the I2S system is active. SDO and SDI are
* always configured even if the system is not open for both
* capture and playback. This ensures that if the DAC is
* not muted, then the output line is held in place, reducing
* the chance of any random data being picked up. CDCLK is
* only selected when the system is in master mode.
*
* When the I2S unit is not being used, all pins are set to
* input, so either the internal resistors need to be configured
* or the lines need to have external pull-up or pull-downs.
*/
//#define DEBUG 1
#include "debug.h"
#include <linux/autoconf.h>
#include <sound/driver.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/pm.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <asm/hardware.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <asm/arch/regs-iis.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/audio.h>
#include "s3c24xx-iis.h"
/* debugging macros */
#ifdef DEBUG
#undef pr_debug
#define pr_debug(fmt,arg...) printk(fmt,##arg)
#endif
#ifndef DEBUG
#define DBG(x...)
#else
#define DBG(x...) do { printk(x); } while(0)
#endif
#ifndef DEBUG
#define DBG_DMA(x...)
#else
#define DBG_DMA(x...) do { printk(x); } while(0)
#endif
static s3c24xx_card_t *our_card;
/* s3c24xx_snd_capture_hw
*
* the default initialiser for the capture hardware stream,
* not to be altered
*/
static struct snd_pcm_hardware s3c24xx_snd_hw = {
.info = ( SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME ),
#if 1
/* only this is supported by our codec */
.formats = SNDRV_PCM_FMTBIT_S16_LE,
#else
.formats = ( SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_U16_LE |
SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S8 ),
#endif
.rates = SNDRV_PCM_RATE_8000_44100, // | SNDRV_PCM_RATE_KNOT,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 64*1024,
.period_bytes_min = 4,
.period_bytes_max = PAGE_SIZE,
.periods_min = 2,
.periods_max = 128,
.fifo_size = 32,
};
static struct s3c2410_dma_client s3c24xx_snd_playback_client = {
.name = "S3C24XX Audio Playback",
};
static struct s3c2410_dma_client s3c24xx_snd_capture_client = {
.name = "S3C24XX Audio Capture",
};
/* debug helpers */
static void s3c24xx_snd_showregs(s3c24xx_card_t *card)
{
pr_debug("--- %s\n", __FUNCTION__);
#ifdef DEBUG
printk(KERN_DEBUG "IIS: CON=%08x, MOD=%08x, PSR=%08x, FCON=%08x\n",
readl(card->regs + S3C2410_IISCON),
readl(card->regs + S3C2410_IISMOD),
readl(card->regs + S3C2410_IISPSR),
readl(card->regs + S3C2410_IISFCON));
#endif
}
static void s3c24xx_snd_showdma(s3c24xx_runtime_t *or)
{
dma_addr_t src, dst;
pr_debug("--- %s\n", __FUNCTION__);
s3c2410_dma_getposition(or->dma_ch, &src, &dst);
DBG("dma position: src=0x%lx, dst=0x%lx\n", (long)src, (long)dst);
}
/* conversion */
static struct s3c24xx_platdata_iis *to_platdata(s3c24xx_card_t *card)
{
pr_debug("--- %s\n", __FUNCTION__);
return (card->device == NULL) ? NULL : card->device->platform_data;
}
/* helpers */
static inline int s3c24xx_snd_is_clkmaster(s3c24xx_card_t *chip)
{
pr_debug("--- %s\n", __FUNCTION__);
return (readl(chip->regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
}
/* audio hardware control */
static void s3c24xx_snd_txctrl(s3c24xx_card_t *card, int on)
{
unsigned long iisfcon;
unsigned long iiscon;
unsigned long iismod;
pr_debug("--- %s\n", __FUNCTION__);
iisfcon = readl(card->regs + S3C2410_IISFCON);
iiscon = readl(card->regs + S3C2410_IISCON);
iismod = readl(card->regs + S3C2410_IISMOD);
if (on) {
iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
iiscon &= ~S3C2410_IISCON_TXIDLE;
iismod |= S3C2410_IISMOD_TXMODE;
iismod |= S3C2410_IISMOD_RXMODE;
if (s3c24xx_snd_is_clkmaster(card))
iiscon |= S3C2410_IISCON_PSCEN;
else
iiscon &= ~S3C2410_IISCON_PSCEN;
writel(iismod, card->regs + S3C2410_IISMOD);
writel(iisfcon, card->regs + S3C2410_IISFCON);
writel(iiscon, card->regs + S3C2410_IISCON);
} else {
/* note, we have to disable the FIFOs otherwise bad things
* seem to happen when the DMA stops. According to the
* Samsung supplied kernel, this should allow the DMA
* engine and FIFOs to reset. If this isn't allowed, the
* DMA engine will simply freeze randomly.
*/
iisfcon &= ~S3C2410_IISFCON_TXENABLE;
iisfcon &= ~S3C2410_IISFCON_TXDMA;
iiscon |= S3C2410_IISCON_TXIDLE;
iiscon &= ~S3C2410_IISCON_TXDMAEN;
//iismod &= ~S3C2410_IISMOD_TXMODE;
if (s3c24xx_snd_is_clkmaster(card))
iiscon |= S3C2410_IISCON_PSCEN;
else
iiscon &= ~S3C2410_IISCON_PSCEN;
writel(iiscon, card->regs + S3C2410_IISCON);
writel(iisfcon, card->regs + S3C2410_IISFCON);
//writel(iismod, card->regs + S3C2410_IISMOD);
}
DBG("%s: iismod=0x%08lx, iisfcon=0x%08lx, iiscon=0x%08lx\n",
__FUNCTION__, iismod, iisfcon, iiscon);
}
static void s3c24xx_snd_rxctrl(s3c24xx_card_t *card, int on)
{
unsigned long iisfcon;
unsigned long iiscon;
unsigned long iismod;
pr_debug("--- %s\n", __FUNCTION__);
iisfcon = readl(card->regs + S3C2410_IISFCON);
iiscon = readl(card->regs + S3C2410_IISCON);
iismod = readl(card->regs + S3C2410_IISMOD);
if (on) {
iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN|
(s3c24xx_snd_is_clkmaster(card)?S3C2410_IISCON_PSCEN:0);
iiscon &= (~S3C2410_IISCON_RXIDLE);
iismod |= S3C2410_IISMOD_RXMODE;
iismod |= S3C2410_IISMOD_TXMODE;
writel(iismod, card->regs + S3C2410_IISMOD);
writel(iisfcon, card->regs + S3C2410_IISFCON);
writel(iiscon, card->regs + S3C2410_IISCON);
} else {
/* note, we have to disable the FIFOs otherwise bad things
* seem to happen when the DMA stops. According to the
* Samsung supplied kernel, this should allow the DMA
* engine and FIFOs to reset. If this isn't allowed, the
* DMA engine will simply freeze randomly.
*/
iisfcon &= ~S3C2410_IISFCON_RXENABLE;
iisfcon &= ~S3C2410_IISFCON_RXDMA;
iiscon |= S3C2410_IISCON_RXIDLE;
iiscon &= ~S3C2410_IISCON_RXDMAEN;
//iismod &= ~S3C2410_IISMOD_RXMODE;
writel(iisfcon, card->regs + S3C2410_IISFCON);
writel(iiscon, card->regs + S3C2410_IISCON);
//writel(iismod, card->regs + S3C2410_IISMOD);
}
DBG("%s: iismod=0x%08lx, iisfcon=0x%08lx, iiscon=0x%08lx\n",
__FUNCTION__, iismod, iisfcon, iiscon);
}
int s3c24xx_iis_cfgclksrc(s3c24xx_card_t *card, const char *name)
{
struct s3c24xx_platdata_iis *pdata = to_platdata(card);
unsigned long iismod;
pr_debug("--- %s\n", __FUNCTION__);
if (name == NULL && pdata != NULL)
name = pdata->codec_clk;
if (name == NULL)
return -EINVAL;
DBG("%s: clock source %s\n", __FUNCTION__, name);
/* alter clock source (master/slave) appropriately */
iismod = readl(card->regs + S3C2410_IISMOD);
if (strcmp(name, "pclk") == 0 || strcmp(name, "cdclk") == 0) {
iismod &= ~S3C2410_IISMOD_SLAVE;
} else {
iismod |= S3C2410_IISMOD_SLAVE;
}
writel(iismod, card->regs + S3C2410_IISMOD);
return 0;
}
EXPORT_SYMBOL(s3c24xx_iis_cfgclksrc);
/* s3c24xx_pcm_enqueue
*
* place a dma buffer onto the queue for the dma system
* to handle.
*/
static void s3c24xx_pcm_enqueue(s3c24xx_runtime_t *or)
{
dma_addr_t pos = or->dma_pos;
int ret;
pr_debug("--- %s\n", __FUNCTION__);
DBG_DMA("%s: pos=%lx, period=%x, end=%lx, loaded=%d\n", __FUNCTION__,
(long)or->dma_start, or->dma_period,
(long)or->dma_end, or->dma_loaded);
do {
DBG_DMA("%s: load buffer %lx\n", __FUNCTION__, (long)pos);
ret = s3c2410_dma_enqueue(or->dma_ch, or, pos, or->dma_period);
if (ret == 0) {
or->dma_loaded++;
pos += or->dma_period;
if (pos >= or->dma_end)
pos = or->dma_start;
}
} while (ret == 0 && or->dma_loaded < or->dma_limit);
or->dma_pos = pos;
}
static void s3c24xx_snd_playback_buffdone(struct s3c2410_dma_chan *ch,
void *bufid, int size,
enum s3c2410_dma_buffresult result)
{
s3c24xx_runtime_t *or = bufid;
pr_debug("--- %s\n", __FUNCTION__);
DBG_DMA("%s: %p,%p, or %p, sz %d, res %d\n",
__FUNCTION__, ch, bufid, or, size, result);
if (or->stream)
snd_pcm_period_elapsed(or->stream);
spin_lock(&or->lock);
if (or->state & ST_RUNNING) {
or->dma_loaded--;
s3c24xx_pcm_enqueue(or);
}
spin_unlock(&or->lock);
}
static void s3c24xx_snd_capture_buffdone(struct s3c2410_dma_chan *ch,
void *bufid, int size,
enum s3c2410_dma_buffresult result)
{
s3c24xx_runtime_t *or = bufid;
pr_debug("--- %s\n", __FUNCTION__);
DBG_DMA("%s: %p,%p, or %p, sz %d, res %d\n",
__FUNCTION__, ch, bufid, or, size, result);
if (or->stream)
snd_pcm_period_elapsed(or->stream);
spin_lock(&or->lock);
if (or->state & ST_RUNNING) {
or->dma_loaded--;
s3c24xx_pcm_enqueue(or);
}
spin_unlock(&or->lock);
}
static int call_startup(struct s3c24xx_iis_ops *ops)
{
pr_debug("--- %s\n", __FUNCTION__);
if (ops && ops->startup)
return (ops->startup)(ops);
return 0;
}
static int call_open(struct s3c24xx_iis_ops *ops,
struct snd_pcm_substream *substream)
{
pr_debug("--- %s\n", __FUNCTION__);
if (ops && ops->open)
return (ops->open)(ops, substream);
return 0;
}
static int s3c24xx_snd_open(struct snd_pcm_substream *substream)
{
s3c24xx_card_t *chip = snd_pcm_substream_chip(substream);
s3c24xx_runtime_t *or;
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
pr_debug("--- %s\n", __FUNCTION__);
DBG("%s: substream=%p, chip=%p\n", __FUNCTION__, substream, chip);
runtime->private_data = &chip->playback;
runtime->hw = s3c24xx_snd_hw;
down(&chip->sem);
/* todo - request dma channel nicely */
DBG("%s: state: playback 0x%x, capture 0x%x\n",
__FUNCTION__, chip->playback.state, chip->capture.state);
if (chip->playback.state == 0 && chip->capture.state == 0) {
/* ensure we hold all the modules we need */
if (chip->base_ops) {
if (try_module_get(chip->base_ops->owner))
chip->base_ops_claimed = 1;
else {
dev_err(chip->device, "cannot claim module\n");
ret = -EINVAL;
goto exit_err;
}
}
if (chip->chip_ops) {
if (try_module_get(chip->chip_ops->owner))
chip->chip_ops_claimed = 1;
else {
dev_err(chip->device, "cannot claim module\n");
ret = -EINVAL;
goto exit_err;
}
}
/* ensure the chip is started */
ret = call_startup(chip->base_ops);
if (ret)
goto exit_err;
ret = call_startup(chip->chip_ops);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -