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

📄 s3c24xx-i2s.c

📁 linux 内核源代码
💻 C
字号:
/* * s3c24xx-i2s.c  --  ALSA Soc Audio Layer * * (c) 2006 Wolfson Microelectronics PLC. * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com * * (c) 2004-2005 Simtec Electronics *	http://armlinux.simtec.co.uk/ *	Ben Dooks <ben@simtec.co.uk> * *  This program is free software; you can redistribute  it and/or modify it *  under  the terms of  the GNU General  Public License as published by the *  Free Software Foundation;  either version 2 of the  License, or (at your *  option) any later version. * * *  Revision history *    11th Dec 2006   Merged with Simtec driver *    10th Nov 2006   Initial version. */#include <linux/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/delay.h>#include <linux/clk.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/initval.h>#include <sound/soc.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/arch/regs-iis.h>#include <asm/arch/regs-gpio.h>#include <asm/arch/regs-clock.h>#include <asm/arch/audio.h>#include <asm/dma.h>#include <asm/arch/dma.h>#include "s3c24xx-pcm.h"#include "s3c24xx-i2s.h"#define S3C24XX_I2S_DEBUG 0#if S3C24XX_I2S_DEBUG#define DBG(x...) printk(KERN_DEBUG x)#else#define DBG(x...)#endifstatic struct s3c2410_dma_client s3c24xx_dma_client_out = {	.name = "I2S PCM Stereo out"};static struct s3c2410_dma_client s3c24xx_dma_client_in = {	.name = "I2S PCM Stereo in"};static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_out = {	.client		= &s3c24xx_dma_client_out,	.channel	= DMACH_I2S_OUT,	.dma_addr	= S3C2410_PA_IIS + S3C2410_IISFIFO,	.dma_size	= 2,};static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = {	.client		= &s3c24xx_dma_client_in,	.channel	= DMACH_I2S_IN,	.dma_addr	= S3C2410_PA_IIS + S3C2410_IISFIFO,	.dma_size	= 2,};struct s3c24xx_i2s_info {	void __iomem	*regs;	struct clk	*iis_clk;};static struct s3c24xx_i2s_info s3c24xx_i2s;static void s3c24xx_snd_txctrl(int on){	u32 iisfcon;	u32 iiscon;	u32 iismod;	DBG("Entered %s\n", __FUNCTION__);	iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);	iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);	iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);	DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);	if (on) {		iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;		iiscon  |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;		iiscon  &= ~S3C2410_IISCON_TXIDLE;		iismod  |= S3C2410_IISMOD_TXMODE;		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);		writel(iiscon,  s3c24xx_i2s.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;		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);	}	DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);}static void s3c24xx_snd_rxctrl(int on){	u32 iisfcon;	u32 iiscon;	u32 iismod;	DBG("Entered %s\n", __FUNCTION__);	iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);	iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);	iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);	DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);	if (on) {		iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;		iiscon  |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;		iiscon  &= ~S3C2410_IISCON_RXIDLE;		iismod  |= S3C2410_IISMOD_RXMODE;		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);		writel(iiscon,  s3c24xx_i2s.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, s3c24xx_i2s.regs + S3C2410_IISFCON);		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);	}	DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);}/* * Wait for the LR signal to allow synchronisation to the L/R clock * from the codec. May only be needed for slave mode. */static int s3c24xx_snd_lrsync(void){	u32 iiscon;	unsigned long timeout = jiffies + msecs_to_jiffies(5);	DBG("Entered %s\n", __FUNCTION__);	while (1) {		iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);		if (iiscon & S3C2410_IISCON_LRINDEX)			break;		if (timeout < jiffies)			return -ETIMEDOUT;	}	return 0;}/* * Check whether CPU is the master or slave */static inline int s3c24xx_snd_is_clkmaster(void){	DBG("Entered %s\n", __FUNCTION__);	return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;}/* * Set S3C24xx I2S DAI format */static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,		unsigned int fmt){	u32 iismod;	DBG("Entered %s\n", __FUNCTION__);	iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);	DBG("hw_params r: IISMOD: %lx \n", iismod);	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {	case SND_SOC_DAIFMT_CBM_CFM:		iismod |= S3C2410_IISMOD_SLAVE;		break;	case SND_SOC_DAIFMT_CBS_CFS:		break;	default:		return -EINVAL;	}	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {	case SND_SOC_DAIFMT_LEFT_J:		iismod |= S3C2410_IISMOD_MSB;		break;	case SND_SOC_DAIFMT_I2S:		break;	default:		return -EINVAL;	}	writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);	DBG("hw_params w: IISMOD: %lx \n", iismod);	return 0;}static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,				struct snd_pcm_hw_params *params){	struct snd_soc_pcm_runtime *rtd = substream->private_data;	u32 iismod;	DBG("Entered %s\n", __FUNCTION__);	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)		rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out;	else		rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_in;	/* Working copies of register */	iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);	DBG("hw_params r: IISMOD: %lx\n", iismod);	switch (params_format(params)) {	case SNDRV_PCM_FORMAT_S8:		break;	case SNDRV_PCM_FORMAT_S16_LE:		iismod |= S3C2410_IISMOD_16BIT;		break;	}	writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);	DBG("hw_params w: IISMOD: %lx\n", iismod);	return 0;}static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd){	int ret = 0;	DBG("Entered %s\n", __FUNCTION__);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_RESUME:	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		if (!s3c24xx_snd_is_clkmaster()) {			ret = s3c24xx_snd_lrsync();			if (ret)				goto exit_err;		}		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)			s3c24xx_snd_rxctrl(1);		else			s3c24xx_snd_txctrl(1);		break;	case SNDRV_PCM_TRIGGER_STOP:	case SNDRV_PCM_TRIGGER_SUSPEND:	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)			s3c24xx_snd_rxctrl(0);		else			s3c24xx_snd_txctrl(0);		break;	default:		ret = -EINVAL;		break;	}exit_err:	return ret;}/* * Set S3C24xx Clock source */static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,	int clk_id, unsigned int freq, int dir){	u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);	DBG("Entered %s\n", __FUNCTION__);	iismod &= ~S3C2440_IISMOD_MPLL;	switch (clk_id) {	case S3C24XX_CLKSRC_PCLK:		break;	case S3C24XX_CLKSRC_MPLL:		iismod |= S3C2440_IISMOD_MPLL;		break;	default:		return -EINVAL;	}	writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);	return 0;}/* * Set S3C24xx Clock dividers */static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,	int div_id, int div){	u32 reg;	DBG("Entered %s\n", __FUNCTION__);	switch (div_id) {	case S3C24XX_DIV_BCLK:		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);		break;	case S3C24XX_DIV_MCLK:		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);		break;	case S3C24XX_DIV_PRESCALER:		writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);		reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);		writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);		break;	default:		return -EINVAL;	}	return 0;}/* * To avoid duplicating clock code, allow machine driver to * get the clockrate from here. */u32 s3c24xx_i2s_get_clockrate(void){	return clk_get_rate(s3c24xx_i2s.iis_clk);}EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);static int s3c24xx_i2s_probe(struct platform_device *pdev){	DBG("Entered %s\n", __FUNCTION__);	s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);	if (s3c24xx_i2s.regs == NULL)		return -ENXIO;	s3c24xx_i2s.iis_clk=clk_get(&pdev->dev, "iis");	if (s3c24xx_i2s.iis_clk == NULL) {		DBG("failed to get iis_clock\n");		iounmap(s3c24xx_i2s.regs);		return -ENODEV;	}	clk_enable(s3c24xx_i2s.iis_clk);	/* Configure the I2S pins in correct mode */	s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);	s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);	s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);	s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);	s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);	writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);	s3c24xx_snd_txctrl(0);	s3c24xx_snd_rxctrl(0);	return 0;}#define S3C24XX_I2S_RATES \	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)struct snd_soc_cpu_dai s3c24xx_i2s_dai = {	.name = "s3c24xx-i2s",	.id = 0,	.type = SND_SOC_DAI_I2S,	.probe = s3c24xx_i2s_probe,	.playback = {		.channels_min = 2,		.channels_max = 2,		.rates = S3C24XX_I2S_RATES,		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},	.capture = {		.channels_min = 2,		.channels_max = 2,		.rates = S3C24XX_I2S_RATES,		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},	.ops = {		.trigger = s3c24xx_i2s_trigger,		.hw_params = s3c24xx_i2s_hw_params,},	.dai_ops = {		.set_fmt = s3c24xx_i2s_set_fmt,		.set_clkdiv = s3c24xx_i2s_set_clkdiv,		.set_sysclk = s3c24xx_i2s_set_sysclk,	},};EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai);/* Module information */MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -