3线接口与wm8976声卡驱动

JZ2440使用WM9876声卡,其硬件原理图:

    其控制接口有2线和3线模式,有MODE管脚决定,高电平为3线模式,低电平为2线模式,我们这里MODE接高电平,
使用3线接口模式。
    3线接口不是L3接口,但与L3接口相似,s3c2410-uda1341.c已经有了L3接口的操作,但没有3线接口的操作,我们把
L3接口的操作修改一下支持3线接口,用3线接口接到的那三个GPIO来模拟3线接口的时序,把对3线的操作抽象出来。

3线信号:
CSB:
SCLK:位时钟(芯片BCK脚)(一时钟一位数据)
SD:数据
说明:
    every rising dege of SCLK in one data bit from the SDIN pin. A rising dege on CSB/GPIO1 pin 
latches in a complete control word consisting of the last 16 bits.
    每次传输16位,传完第16位后,产生一个CSB上升沿,代表一次数据传输完毕。每位数据在SCLK的上升沿传输。
3线控制信号一个控制字由16 bit组成,这 16 bit 数据的含义:前7位(bit 15 : bit 9):是地址选择位,选择芯
片的那个寄存器操作。余下9位(bit 8 : bit 0)是数据位,写到寄存器对应的各位。
    传输顺序是先高位后低位,与UDA1341顺序相反。
3线控制信号的数据发送时序图:

对s3c2410-uda1341.c文件进行修改,完成下面3步,使支持3线接口:

1、添加wm8976写寄存器函数wm8976_write_reg(),根据时序图,用与3线相接的GPIO模拟数据发送时的时序。

static void wm8976_write_reg(unsigned char reg, unsigned int data)
{
	int i;

	unsigned long flags;
	unsigned short val = (reg << 9) | (data & 0x1ff);// 将寄存器和数据合并成16bit

	//首先把三条线都输出为高电平
	s3c2410_gpio_setpin(S3C2410_GPB2,1);//S3C2410_GPB2:L3MODE
	s3c2410_gpio_setpin(S3C2410_GPB3,1);//S3C2410_GPB3: L3DATA
	s3c2410_gpio_setpin(S3C2410_GPB4,1);//S3C2410_GPB4: L3CLOCK
	local_irq_save(flags); // 先关中断
	
	//将合并好的16位数据,依次发出,在时钟的上升沿获得数据
	for (i = 0; i < 16; i++) { // WM8976是先发出bit15位
		if (val & (1<<15)) // 如果是第15位
		{
			// 先给L3CLOCK上升沿,然后拉高L3DATA线
			s3c2410_gpio_setpin(S3C2410_GPB4,0);
			// 同上,L3DATA拉高
			s3c2410_gpio_setpin(S3C2410_GPB3,1); 
			udelay(1); // 延时一会
			// 拉高L3CLOCK
			s3c2410_gpio_setpin(S3C2410_GPB4,1); 
		}
		else
		{  	// 先给L3CLOCK上升沿,过段时间拉低L3DATA线
			s3c2410_gpio_setpin(S3C2410_GPB4,0);
			s3c2410_gpio_setpin(S3C2410_GPB3,0);
			udelay(1);
			s3c2410_gpio_setpin(S3C2410_GPB4,1); 
		}
		
		val = val << 1; // 左移一位,发出下一位的寄存器数据
	}
	
	//还需要wm8976_write_reg()最后加上:
	s3c2410_gpio_setpin(S3C2410_GPB2,0);// 拉低L3MODE
	udelay(1); // 延时一会
	s3c2410_gpio_setpin(S3C2410_GPB2,1);// S3C2410_GPB2:L3MODE
	// 为稳定,拉高L3DATA线
	s3c2410_gpio_setpin(S3C2410_GPB3,1);
	s3c2410_gpio_setpin(S3C2410_GPB4,1);// 同上,拉高L3CLOCK
	local_irq_restore(flags); // 开中断
}

2、添加芯片初始化函数init_wm8976(),往哪个寄存器写什么值,写寄存器的顺序,参考芯片手册page88:

 

static void init_wm8976(void)
{
	uda1341_volume = 57;
	uda1341_boost = 0;

	/* software reset */
	wm8976_write_reg(0, 0);

	/* OUT2的左/右声道打开
	 * 左/右通道输出混音打开
	 * 左/右DAC打开
	 */
	wm8976_write_reg(0x3, 0x6f);
	
	wm8976_write_reg(0x1, 0x1f);//biasen,BUFIOEN.VMIDSEL=11b  
	wm8976_write_reg(0x2, 0x185);//ROUT1EN LOUT1EN, inpu PGA enable ,ADC enable

	wm8976_write_reg(0x6, 0x0);//SYSCLK=MCLK  
	wm8976_write_reg(0x4, 0x10);//16bit 		
	wm8976_write_reg(0x2B,0x10);//BTL OUTPUT  
	wm8976_write_reg(0x9, 0x50);//Jack detect enable  
	wm8976_write_reg(0xD, 0x21);//Jack detect  
	wm8976_write_reg(0x7, 0x01);//Jack detect 
}

3、修改一下音量的调节,也是往某个寄存器写值实现。音量值范围:ioctl:0-100,对应芯片:63-0。要调一下比例。

 

static int smdk2410_mixer_ioctl(struct inode *inode, struct file *file,
				unsigned int cmd, unsigned long arg)
{
	int ret;
	long val = 0;

	switch (cmd) {
		case SOUND_MIXER_INFO:
		{
			mixer_info info;
			strncpy(info.id, "UDA1341", sizeof(info.id));
			strncpy(info.name,"Philips UDA1341", sizeof(info.name));
			info.modify_counter = audio_mix_modcnt;
			return copy_to_user((void *)arg, &info, sizeof(info));
		}

		case SOUND_OLD_MIXER_INFO:
		{
			_old_mixer_info info;
			strncpy(info.id, "UDA1341", sizeof(info.id));
			strncpy(info.name,"Philips UDA1341", sizeof(info.name));
			return copy_to_user((void *)arg, &info, sizeof(info));
		}

		case SOUND_MIXER_READ_STEREODEVS:
			return put_user(0, (long *) arg);

		case SOUND_MIXER_READ_CAPS:
			val = SOUND_CAP_EXCL_INPUT;
			return put_user(val, (long *) arg);

		case SOUND_MIXER_WRITE_VOLUME:
			ret = get_user(val, (long *) arg);
			if (ret)
				return ret;

			/* ioctl: val越大表示音量越大, 0-最小, 100-最大
			 * UDA1341: 寄存器的值越小音量越大
			 * WM8976: 52,53号寄存器bit[5:0]表示音量, 值越大音量越大, 0-63
			 */
			
			uda1341_volume = (((val & 0xff) + 1) * 63) / 100;
			wm8976_write_reg(52, (1<<8)|uda1341_volume);
			wm8976_write_reg(53, (1<<8)|uda1341_volume);
			//uda1341_l3_address(UDA1341_REG_DATA0);
			//uda1341_l3_data(uda1341_volume);
			break;

		case SOUND_MIXER_READ_VOLUME:
			val = (uda1341_volume * 100) / 63;
			return put_user(val, (long *) arg);

		case SOUND_MIXER_READ_IGAIN:
			val = ((31- mixer_igain) * 100) / 31;
			return put_user(val, (int *) arg);

		case SOUND_MIXER_WRITE_IGAIN:
			ret = get_user(val, (int *) arg);
			if (ret)
				return ret;
			mixer_igain = 31 - (val * 31 / 100);
			/* use mixer gain channel 1*/
			//uda1341_l3_address(UDA1341_REG_DATA0);
			//uda1341_l3_data(EXTADDR(EXT0));
			//uda1341_l3_data(EXTDATA(EXT0_CH1_GAIN(mixer_igain)));
			break;

		default:
			DPRINTK("mixer ioctl %u unknown\n", cmd);
			return -ENOSYS;
	}

	audio_mix_modcnt++;
	return 0;
}

把修改好的s3c2410-uda1341.c另存为s3c-wm8976.c。

测试:
1. 确定内核里已经配置了sound\soc\s3c24xx\s3c2410-uda1341.c
-> Device Drivers
  -> Sound
    -> Advanced Linux Sound Architecture  // 兼容OSS
      -> Advanced Linux Sound Architecture
        -> System on Chip audio support
        <*> I2S of the Samsung S3C24XX chips
2. 修改sound/soc/s3c24xx/Makefile
obj-y += s3c2410-uda1341.o
改为:
obj-y += s3c-wm8976.o   
3. make uImage
   使用新内核启动
4. ls -l /dev/dsp /dev/mixer
5. 播放:
   在WINDOWS PC里找一个wav文件,放到开发板根文件系统里
   cat Windows.wav > /dev/dsp
6. 录音:
   cat /dev/dsp > sound.bin  
   然后对着麦克风说话
   ctrl+c退出

   cat sound.bin > /dev/dsp  // 就可以听到录下的声音   

参考文章:L3接口与UDA1341声卡测试点击打开链接

 

 

 

 

 

 

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值