⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3c24xx-iis_c.txt

📁 2410uda1341的测试代码
💻 TXT
📖 第 1 页 / 共 3 页
字号:
/* 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 + -