S3C2440 音频解码芯片WM8976声卡驱动移植、madplay测试

2023-09-12  

1. WM9876接口和工作原理


本节使用了JZ2440开发板移植WM9876驱动,其结构如下图所示,最后利用madplay工具测试音频文件。

17-1

IIS和控制接口;


WM9876声卡是负责录音、播音、调节音量和声音合成等的一种多媒体板卡。包括两种接口:IIS接口(提供音频接收和发送)、控制接口(控制音量大小,使能各个输出通道等)

17-2

1)当我们播放声音时,将数字信号传入I2SDO脚,声卡便通过解码,产生模拟信号到喇叭/耳机;


2)当我们录音时,声卡便获取麦克风的模拟信号,编码出数字信号到I2SDI引脚上。


-----------

接口说明

----------------

 

-----------

接口说明

----------------

 

--> IIS接口相关的引脚如下:


  MCLK:主机为解码芯片提供的系统同步时钟(Master/system clock input)。  


  BCLK(LRC):编解码芯片提供的串行时钟信号(Audio bit clock output)。


  ISLRCK:采样频率信号,当为低电平时采样的是左声道信号,高电平时采样的是右声道信号。


  I2SDI(ADCDAT):ADC数据输入。


  I2SDO(DACDAT):DAC数据输出。


-->控制接口相关的引脚如下:


 MODE: 3

线

/2

线控制选择

,

MODE

为高

,

表示为

3

线控制

,MODE

位低

,

表示

2

线控制,

2

线模式变为

IIC

模式

2440

接的高电平为

3

线模式

); 


 CSB/GPIO1: 

控制数据使能引脚; 


 SCLK:  

时钟引脚; 

 

SDIN:  

数据输入输出引脚。

 MODE: 3

线

/2

线控制选择

,

MODE

为高

,

表示为

3

线控制

,MODE

位低

,

表示

2

线控制,

2

线模式变为

IIC

模式

2

线模式变为

IIC

模式

2440

接的高电平为

3

线模式

); 


 CSB/GPIO1: 

控制数据使能引脚; 


 SCLK:  

时钟引脚; 

 

SDIN:  

数据输入输出引脚。

2440

接的高电平为

3

线模式

); 

 CSB/GPIO1: 

 CSB/GPIO1: 

控制数据使能引脚; 


 SCLK:  

时钟引脚; 

 

SDIN:  

 SCLK:  

时钟引脚; 

 

SDIN:  

SDIN:  

数据输入输出引脚。

数据输入输出引脚。

数据输入输出引脚。

2. WM9876驱动移植


2.1 驱动分析


先以uda1341.c

源码为例,分析

驱动程序框架。源码

目录位于:

驱动程序框架。源码

soundsocs3c24xxs3c2410-uda1341.c ,

分析:

1. s3c2410_uda1341_init


 static int __init s3c2410_uda1341_init(void) {

     memzero(&input_stream, sizeof(audio_stream_t));

     memzero(&output_stream, sizeof(audio_stream_t));

     return driver_register(&s3c2410iis_driver); //注册

 }

-->

 static struct device_driver s3c2410iis_driver = {

 .name = "s3c2410-iis",

 .bus = &platform_bus_type, //platform_bus_type类型

 .probe = s3c2410iis_probe,

 .remove = s3c2410iis_remove,

};


2. s3c2410iis_probe

  

由于同名驱动和设备,调用

s3c2410iis_probe:

由于同名驱动和设备,调用

s3c2410iis_probe:


 1 static int s3c2410iis_probe(struct device *dev) 

 2 {

 3     struct platform_device *pdev = to_platform_device(dev);

 4     struct resource *res;

 5     unsigned long flags; 8     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

10     iis_base = (void *)S3C24XX_VA_IIS ;

12     iis_clock = clk_get(dev, "iis");


14     clk_enable(iis_clock); 

16     local_irq_save(flags);

17     /* 配置GPIO */

18     /* GPB 4: L3CLOCK, OUTPUT */

19     s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP);

20     s3c2410_gpio_pullup(S3C2410_GPB4,1);

21     /* GPB 3: L3DATA, OUTPUT */

22     s3c2410_gpio_cfgpin(S3C2410_GPB3,S3C2410_GPB3_OUTP);

23     /* GPB 2: L3MODE, OUTPUT */

24     s3c2410_gpio_cfgpin(S3C2410_GPB2,S3C2410_GPB2_OUTP);

25     s3c2410_gpio_pullup(S3C2410_GPB2,1);

26     /* GPE 3: I2SSDI */

27     s3c2410_gpio_cfgpin(S3C2410_GPE3,S3C2410_GPE3_I2SSDI);

28     s3c2410_gpio_pullup(S3C2410_GPE3,0);

29     /* GPE 0: I2SLRCK */

30     s3c2410_gpio_cfgpin(S3C2410_GPE0,S3C2410_GPE0_I2SLRCK);

31     s3c2410_gpio_pullup(S3C2410_GPE0,0);

32     /* GPE 1: I2SSCLK */

33     s3c2410_gpio_cfgpin(S3C2410_GPE1,S3C2410_GPE1_I2SSCLK);

34     s3c2410_gpio_pullup(S3C2410_GPE1,0);

35     /* GPE 2: CDCLK */

36     s3c2410_gpio_cfgpin(S3C2410_GPE2,S3C2410_GPE2_CDCLK);

37     s3c2410_gpio_pullup(S3C2410_GPE2,0);

38     /* GPE 4: I2SSDO */

39     s3c2410_gpio_cfgpin(S3C2410_GPE4,S3C2410_GPE4_I2SSDO);

40     s3c2410_gpio_pullup(S3C2410_GPE4,0);

41 

42     local_irq_restore(flags);

43   

44     init_s3c2410_iis_bus();///* 设置S3C2440的IIS控制器 */

45 

46     init_uda1341();// /* 使用L3接口初始化uda1341芯片 */

47     

    /* 设置两个DMA通道:一个用于播放,另一个用于录音 */

48     output_stream.dma_ch = DMACH_I2S_OUT;

49     if (audio_init_dma(&output_stream, "UDA1341 out")) {

50         audio_clear_dma(&output_stream,&s3c2410iis_dma_out);

52     }

54     input_stream.dma_ch = DMACH_I2S_IN;

55     if (audio_init_dma(&input_stream, "UDA1341 in")) {

56         audio_clear_dma(&input_stream,&s3c2410iis_dma_in);

57     }

59     audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1); //

        -->sound_insert_unit(&chains[3], fops, dev, 3, 131,

     "dsp", S_IWUSR | S_IRUSR, NULL);


60     audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1); //

       -->sound_insert_unit(&chains[0], fops, dev, 0, 128,

     "mixer", S_IRUSR | S_IWUSR, NULL);

61         ......      

 }


其中, /dev/dsp

设备节点,实现音频的输入输出

 IIS

接口(由

chains[3]

数组获得)


   /dev/mixer 

设备节点,实现音量调节、高音等控制(由

chains[0]

数组获得)

 audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1); 

 audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1);注册了这两个设备节点和file_operation供上层驱动函数调用;

   /dev/mixer 

设备节点,实现音量调节、高音等控制(由

chains[0]

数组获得)

 audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1); 

 audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1);注册了这两个设备节点和file_operation供上层驱动函数调用;

static struct sound_unit *chains[SOUND_STEP];

/*

 *  Allocations

 *  0   *16     Mixers

 *  1   *8      Sequencers

 *  2   *16     Midi

 *  3   *16     DSP

 *  4   *16     SunDSP

 *  5   *16     DSP16

 *  6   --      sndstat (obsolete)

 *  7   *16     unused

 *  8   --      alternate sequencer (see above)

 *  9   *16     raw synthesizer access

 *  10  *16     unused

 *  11  *16     unused

 *  12  *16     unused

 *  13  *16     unused

 *  14  *16     unused

 *  15  *16     unused

 */

 接下来,看一次操作函数,源码程序:linux-2.6.22.6soundSound_core.c,(也可从该源码看起,再看soundsocs3c24xxs3c2410-uda1341.c程序),仅有一个open往后程序可得其他的操作函数:


static const struct file_operations soundcore_fops=

{

    /* We must have an owner or the module locking fails */

    .owner    = THIS_MODULE,

    .open    = soundcore_open,

};


 1 int soundcore_open(struct inode *inode, struct file *file)

 2 {

 3     int chain;

 4     int unit = iminor(inode);

 5     struct sound_unit *s;

 6     const struct file_operations *new_fops = NULL;

 7 

 8     chain=unit&0x0F;

 9     if(chain==4 || chain==5)    /* dsp/audio/dsp16 */

10     {

11         unit&=0xF0;

12         unit|=3;

13         chain=3;

14     }

15     

16     spin_lock(&sound_loader_lock);

17     s = __look_for_unit(chain, unit);

18     if (s)

19         new_fops = fops_get(s->unit_fops);

20     if (!new_fops) {

21         spin_unlock(&sound_loader_lock);

22         request_module("sound-slot-%i", unit>>4);

23         request_module("sound-service-%i-%i", unit>>4, chain);

24         spin_lock(&sound_loader_lock);

25         s = __look_for_unit(chain, unit);

26         if (s)

27             new_fops = fops_get(s->unit_fops);

28     }

29     if (new_fops) {

30         int err = 0;

31         const struct file_operations *old_fops = file->f_op;

32         file->f_op = new_fops;

33         spin_unlock(&sound_loader_lock);

34         if(file->f_op->open)

35             err = file->f_op->open(inode,file);

36         if (err) {

37             fops_put(file->f_op);

38             file->f_op = fops_get(old_fops);

39         }

40         fops_put(old_fops);

41         return err;

42     }

43     spin_unlock(&sound_loader_lock);

44     return -ENODEV;

45 }


函数框架:app: open () // 假设主设备号为14


          soundcore_open函数            


       --> int unit = iminor(inode);              


       s = __look_for_unit(chain, unit);             


           // 从chains数组里得到, 谁来设置这个数组?                                                          

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。