Linux音频驱动之四:I2S 总线操作接口

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_38696651/article/details/89432003

本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记

一. I2S driver 的probe函数
  • 映射虚拟内存,IIS寄存器的起始地址是:0x55000000
s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
  • 获取iis时钟,并使能iis时钟
s3c24xx_i2s.iis_clk = clk_get(&pdev->dev, "iis");
clk_enable(s3c24xx_i2s.iis_clk);
  • 配置IIS引脚,IIS有5个引脚,分别是:
    I2SLRCK: 表示当前传的声音的声道,I2SLRCK 是低电平时表示传的是“左声道”数据,I2SLRCK 是高电平时传的是“右声道”数据。
    I2SSCLK:串行位时钟,I2S传输数据使用
    SCDCLK: 2440 提供给解码芯片使用的系统时钟。
    I2SSDI:I2S输入信号线
    I2SSDO:I2S输出信号线
#define S3C2410_GPE0_I2SLRCK   (0x02 << 0)
#define S3C2410_GPE1_I2SSCLK   (0x02 << 2)
#define S3C2410_GPE2_CDCLK     (0x02 << 4)
#define S3C2410_GPE3_I2SSDI    (0x02 << 6)
#define S3C2410_GPE4_I2SSDO    (0x02 << 8)
/* 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);

GPECON寄存器:
在这里插入图片描述

  • I2S enable, IISCON寄存器的第0位写1
#define S3C2410_IISCON_IISEN	  (1<<0)
writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);

在这里插入图片描述

  • 先控制I2S的发送和接收停止,先不发送和接收。
s3c24xx_snd_txctrl(0);
s3c24xx_snd_rxctrl(0);
二. I2S的发送控制
  • 启动I2S的发送
    ①写 IISCON寄存器,I2S使能,DMA发送请求enable,IISLRCK active
#define S3C2410_IISCON_TXDMAEN	  (1<<5)
#define S3C2410_IISCON_IISEN	  (1<<0)
#define S3C2410_IISCON_TXIDLE	  (1<<3)
iiscon  |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
iiscon  &= ~S3C2410_IISCON_TXIDLE;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

②写IISFCON寄存器,使能发送缓存,缓存模式为DMA模式

#define S3C2410_IISFCON_TXDMA	  (1<<15)
#define S3C2410_IISFCON_TXENABLE  (1<<13)
iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;

在这里插入图片描述

③写 IISMOD寄存器,选择I2S的发送模式。

#define S3C2410_IISMOD_TXMODE	  (2<<6)
iismod  |= S3C2410_IISMOD_TXMODE;

在这里插入图片描述

  • 停止I2S的发送
    停止I2S几乎是跟I2S的发送的控制相反,唯独的区别是没有将I2S disable。
三. I2S的接收控制
  • 启动I2S的接收
    ①写 IISCON寄存器,I2S使能,DMA接收请求enable,IISLRCK active
#define S3C2410_IISCON_RXDMAEN	  (1<<4)
#define S3C2410_IISCON_IISEN	  (1<<0)
#define S3C2410_IISCON_RXIDLE	  (1<<2)
iiscon  |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
iiscon  &= ~S3C2410_IISCON_RXIDLE;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

②写 IISMOD寄存器,选择I2S的接收模式。

#define S3C2410_IISMOD_RXMODE	  (1<<6)
iismod  |= S3C2410_IISMOD_RXMODE;

在这里插入图片描述

③写IISFCON寄存器,使能接收缓存,缓存模式为DMA模式

#define S3C2410_IISFCON_RXDMA	  (1<<14)
#define S3C2410_IISFCON_RXENABLE  (1<<12)
iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
  • 停止I2S的接收
    跟启动的寄存器写相反的值。
四. I2S的模式设置
  • Master/slave模式设置, IISMOD寄存器的第8位设置
    master模式:cpu向音频解码芯片提供时钟
    slave模式:音频解码芯片提供时钟
iismod |= S3C2410_IISMOD_SLAVE;
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);

在这里插入图片描述

  • 设置串行数据格式,IISMOD寄存器的第4位设置
"#define S3C2410_IISMOD_MSB"	"  (1<<4)"
iismod |= S3C2410_IISMOD_MSB; 

在这里插入图片描述
该芯片只提供两种数据格式,一种是 MSB (LEFT) JUSTIFIED,另外一种是标准的IIS格式。
IIS 格式数据是经过一个 SCLK 位时钟后,才输入第一个数据"MSB"(SD 上空了一个 SCLK时钟)。最后也是以“MSB”结束。

MSB 格式是在第一个 SCLK 时钟就立刻输出了第一个数据。

五. I2S的s3c24xx_i2s_hw_params函数
  • 设置s3c24xx_i2s_hw_params.dai.cpu_dai.dma_data,假设现在是播放。
    s3c24xx_i2s_hw_params.dai.cpu_dai.dma_data = s3c24xx_i2s_pcm_stereo_out
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,
};

IIS FIFO寄存器的地址是 0x55000010,寄存器的大小是2字节。
在这里插入图片描述

  • 设置传输每次传输多少位数据
#define S3C2410_IISMOD_16BIT	  (1<<3)
iismod |= S3C2410_IISMOD_16BIT;
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);

在这里插入图片描述

六. s3c24xx_i2s_trigger函数
  • 看播放状态,判断substream的方向,播放的话调用s3c24xx_snd_txctrl(1);录制调用s3c24xx_snd_rxctrl(1);
  • 启动DMA播放:s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
七. IIS时钟设置
  • 时钟源设置成 PCLK或者 MPLLin
#define S3C2440_IISMOD_MPLL	  (1<<9)
iismod |= S3C2440_IISMOD_MPLL;
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);

在这里插入图片描述

  • 设置分频系数
writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);

在这里插入图片描述
在这里插入图片描述

  • 设置master时钟频率,是256fs还是384fs
reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);

在这里插入图片描述

综上,采样率 = (PCLK or MPLLin)/f分频系数/(256 or 384)

  • 设置串行时钟频率
reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);

在这里插入图片描述

八. I2S接口注册
  • 注册时间:在启动内核是调用s3c24xx_i2s_init函数。在s3c24xx_i2s_init函数中注册I2S接口。
module_init(s3c24xx_i2s_init);

static int __init s3c24xx_i2s_init(void)
{
	return snd_soc_register_dai(&s3c24xx_i2s_dai);
}
  • 注册到哪里:注册到一个dai_list的全局链表中,dai_list链表中是各种各样的dai接口。
int snd_soc_register_dai(struct snd_soc_dai *dai)
{
	if (!dai->name)
		return -EINVAL;

	/* The device should become mandatory over time */
	if (!dai->dev)
		printk(KERN_WARNING "No device for DAI %s\n", dai->name);

	if (!dai->ops)
		dai->ops = &null_dai_ops;

	INIT_LIST_HEAD(&dai->list);

	mutex_lock(&client_mutex);
	list_add(&dai->list, &dai_list);
	snd_soc_instantiate_cards();
	mutex_unlock(&client_mutex);

	pr_debug("Registered DAI '%s'\n", dai->name);

	return 0;
}
九. 总结
  • s3c24xx_i2s_dai接口在内核起来时注册。
  • s3c24xx_i2s_dai接口包含对芯片的I2S总线的波特率,数据传输模式等设置,一个硬件参数操作集s3c24xx_i2s_dai_ops。

参考博客:
https://blog.csdn.net/gqb_driver/article/details/8551551

猜你喜欢

转载自blog.csdn.net/weixin_38696651/article/details/89432003