天嵌科技 ARM开发社区

 找回密码
 注册

【原创】tq6410 alsa声卡驱动框架分析第三章《Wm9713的codec 部分的分析》——[1]-菜鸟 ...

已有 2494 次阅读2012-9-5 06:58 | 框架, 声卡驱动

三.Wm9713codec 部分的分析

         欢迎加入qq138581843讨论,提供所有设计的代码

1.  codec

 

再次引用上面提到的内容由于在sound\soc\samsung\smdk_wm97

13.c文件的初始化函数smdk_init 中注册一个名词为"soc-audio"的平台设备(device

smdk_snd_wm9713_device =

 platform_device_alloc("wm9713-codec", -1);//创建名字为wm9713-codec的设备

根据匹配规则相应的就应该有一个同名的平台驱动(driver),平台驱动注册在文件sound\soc\codecs\wm9713.c

 

static struct platform_driver wm9713_codec_driver = {

      .driver = {

                 .name = "wm9713-codec",//这里定义名字为“wm9713-codec”平台驱动

                 .owner = THIS_MODULE,

      },

 

      .probe = wm9713_probe,//制定probe函数为当前文件的wm9713_probe函数

      .remove = __devexit_p(wm9713_remove),

};

SSss

wm9713_probe的主体

 

snd_soc_register_codec(&pdev->dev,&soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai)

 

这个函数主要设置注册wm9713的解码器,从而引出两个结构体snd_soc_dai_driversnd_soc_codec_drive。在说明这两个结构体之前,先介绍一下snd_soc_codecsnd_soc_codec_driversnd_soc_daisnd_soc_dai_driver,其中的snd_soc_daisnd_soc_dai_driverASoCPlatform驱动中也会使用到,PlatformCodecDAI通过snd_soc_dai_link结构,在Machine驱动中进行绑定连接。下面详细贴出这四个结构体涉及到的成员说明

 

snd_soc_codec:

1.      /* SoC Audio Codec device */  

2.      struct snd_soc_codec {  

3.          const char *name;  /* Codec的名字*/  

4.          struct device *dev; /* 指向Codec设备的指针 */  

5.          const struct snd_soc_codec_driver *driver; /* 指向该codec的驱动的指针 */  

6.          struct snd_soc_card *card;    /* 指向Machine驱动的card实例 */  

7.          int num_dai; /* Codec数字接口的个数,目前越来越多的Codec带有多个I2S或者是PCM接口 */  

8.          int (*volatile_register)(...);  /* 用于判定某一寄存器是否是volatile */  

9.          int (*readable_register)(...);  /* 用于判定某一寄存器是否可读 */  

10.      int (*writable_register)(...);  /* 用于判定某一寄存器是否可写 */  

11.    

12.      /* runtime */  

13.      ......  

14.      /* codec IO */  

15.      void *control_data; /* 该指针指向的结构用于对codec的控制,通常和readwrite字段联合使用 */  

16.      enum snd_soc_control_type control_type;/* 可以是SND_SOC_SPISND_SOC_I2CSND_SOC_REGMAP中的一种 */  

17.      unsigned int (*read)(struct snd_soc_codec *, unsigned int);  /* 读取Codec寄存器的函数 */  

18.      int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);  /* 写入Codec寄存器的函数 */  

19.      /* dapm */  

20.      struct snd_soc_dapm_context dapm;  /* 用于DAPM控件 */  

21.  };  

/* SoC Audio Codec device */

struct snd_soc_codec {

  const char *name;  /* Codec的名字*/

  struct device *dev; /* 指向Codec设备的指针 */

  const struct snd_soc_codec_driver *driver; /* 指向该codec的驱动的指针 */

  struct snd_soc_card *card;    /* 指向Machine驱动的card实例 */

  int num_dai; /* Codec数字接口的个数,目前越来越多的Codec带有多个I2S或者是PCM接口 */

  int (*volatile_register)(...);  /* 用于判定某一寄存器是否是volatile */

  int (*readable_register)(...);  /* 用于判定某一寄存器是否可读 */

  int (*writable_register)(...);  /* 用于判定某一寄存器是否可写 */

 

  /* runtime */

  ......

  /* codec IO */

  void *control_data; /* 该指针指向的结构用于对codec的控制,通常和readwrite字段联合使用 */

  enum snd_soc_control_type control_type;/* 可以是SND_SOC_SPISND_SOC_I2CSND_SOC_REGMAP中的一种 */

  unsigned int (*read)(struct snd_soc_codec *, unsigned int);  /* 读取Codec寄存器的函数 */

  int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);  /* 写入Codec寄存器的函数 */

  /* dapm */

  struct snd_soc_dapm_context dapm;  /* 用于DAPM控件 */

};


snd_soc_codec_driver:

1.      /* codec driver */  

2.      struct snd_soc_codec_driver {  

3.          /* driver ops */  

4.          int (*probe)(struct snd_soc_codec *);  /* codec驱动的probe函数,由snd_soc_instantiate_card回调 */  

5.          int (*remove)(struct snd_soc_codec *);    

6.          int (*suspend)(struct snd_soc_codec *);  /* 电源管理 */  

7.          int (*resume)(struct snd_soc_codec *);  /* 电源管理 */  

8.        

9.          /* Default control and setup, added after probe() is run */  

10.      const struct snd_kcontrol_new *controls;  /* 音频控件指针 */  

11.      const struct snd_soc_dapm_widget *dapm_widgets;  /* dapm部件指针 */  

12.      const struct snd_soc_dapm_route *dapm_routes;  /* dapm路由指针 */  

13.    

14.      /* codec wide operations */  

15.      int (*set_sysclk)(...);  /* 时钟配置函数 */  

16.      int (*set_pll)(...);  /* 锁相环配置函数 */  

17.    

18.      /* codec IO */  

19.      unsigned int (*read)(...);  /* 读取codec寄存器函数 */  

20.      int (*write)(...);  /* 写入codec寄存器函数 */  

21.      int (*volatile_register)(...);  /* 用于判定某一寄存器是否是volatile */  

22.      int (*readable_register)(...);  /* 用于判定某一寄存器是否可读 */  

23.      int (*writable_register)(...);  /* 用于判定某一寄存器是否可写 */  

24.    

25.      /* codec bias level */  

26.      int (*set_bias_level)(...);  /* 偏置电压配置函数 */  

27.    

28.  };  

/* codec driver */

struct snd_soc_codec_driver {

/* driver ops */

int (*probe)(struct snd_soc_codec *);  /* codec驱动的probe函数,由snd_soc_instantiate_card回调 */

int (*remove)(struct snd_soc_codec *); 

int (*suspend)(struct snd_soc_codec *);  /* 电源管理 */

int (*resume)(struct snd_soc_codec *);  /* 电源管理 */

 

/* Default control and setup, added after probe() is run */

const struct snd_kcontrol_new *controls;  /* 音频控件指针 */

const struct snd_soc_dapm_widget *dapm_widgets;  /* dapm部件指针 */

const struct snd_soc_dapm_route *dapm_routes;  /* dapm路由指针 */

 

/* codec wide operations */

int (*set_sysclk)(...);  /* 时钟配置函数 */

int (*set_pll)(...);  /* 锁相环配置函数 */

 

/* codec IO */

unsigned int (*read)(...);  /* 读取codec寄存器函数 */

int (*write)(...);  /* 写入codec寄存器函数 */

int (*volatile_register)(...);  /* 用于判定某一寄存器是否是volatile */

int (*readable_register)(...);  /* 用于判定某一寄存器是否可读 */

int (*writable_register)(...);  /* 用于判定某一寄存器是否可写 */

 

/* codec bias level */

int (*set_bias_level)(...);  /* 偏置电压配置函数 */

 

};

snd_soc_dai:

1.      /*  

2.       * Digital Audio Interface runtime data.  

3.       *  

4.       * Holds runtime data for a DAI.  

5.       */  

6.      struct snd_soc_dai {  

7.          const char *name;  /* dai的名字 */  

8.          struct device *dev;  /* 设备指针 */  

9.        

10.      /* driver ops */  

11.      struct snd_soc_dai_driver *driver;  /* 指向dai驱动结构的指针 */  

12.    

13.      /* DAI runtime info */  

14.      unsigned int capture_active:1;      /* stream is in use */  

15.      unsigned int playback_active:1;     /* stream is in use */  

16.    

17.      /* DAI DMA data */  

18.      void *playback_dma_data;  /* 用于管理playback dma */  

19.      void *capture_dma_data;  /* 用于管理capture dma */  

20.    

21.      /* parent platform/codec */  

22.      union {  

23.          struct snd_soc_platform *platform;  /* 如果是cpu dai,指向所绑定的平台 */  

24.          struct snd_soc_codec *codec;  /* 如果是codec dai指向所绑定的codec */  

25.      };  

26.      struct snd_soc_card *card;  /* 指向Machine驱动中的crad实例 */  

27.  };  

/*

 * Digital Audio Interface runtime data.

 *

 * Holds runtime data for a DAI.

 */

struct snd_soc_dai {

const char *name;  /* dai的名字 */

struct device *dev;  /* 设备指针 */

 

/* driver ops */

struct snd_soc_dai_driver *driver;  /* 指向dai驱动结构的指针 */

 

/* DAI runtime info */

unsigned int capture_active:1;  /* stream is in use */

unsigned int playback_active:1;  /* stream is in use */

 

/* DAI DMA data */

void *playback_dma_data;  /* 用于管理playback dma */

void *capture_dma_data;  /* 用于管理capture dma */

 

/* parent platform/codec */

union {

  struct snd_soc_platform *platform;  /* 如果是cpu dai,指向所绑定的平台 */

  struct snd_soc_codec *codec;  /* 如果是codec dai指向所绑定的codec */

};

struct snd_soc_card *card;  /* 指向Machine驱动中的crad实例 */

};

snd_soc_dai_driver:

1.      /*  

2.       * Digital Audio Interface Driver.  

3.       *  

4.       * Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97  

5.       * operations and capabilities. Codec and platform drivers will register this  

6.       * structure for every DAI they have.  

7.       *  

8.       * This structure covers the clocking, formating and ALSA operations for each  

9.       * interface.  

10.   */  

11.  struct snd_soc_dai_driver {  

12.      /* DAI description */  

13.      const char *name;  /* dai驱动名字 */  

14.    

15.      /* DAI driver callbacks */  

16.      int (*probe)(struct snd_soc_dai *dai);  /* dai驱动的probe函数,由snd_soc_instantiate_card回调 */  

17.      int (*remove)(struct snd_soc_dai *dai);    

18.      int (*suspend)(struct snd_soc_dai *dai);  /* 电源管理 */  

19.      int (*resume)(struct snd_soc_dai *dai);    

20.    

21.      /* ops */  

22.      const struct snd_soc_dai_ops *ops;  /* 指向本daisnd_soc_dai_ops结构 */  

23.    

24.      /* DAI capabilities */  

25.      struct snd_soc_pcm_stream capture;  /* 描述capture的能力 */  

26.      struct snd_soc_pcm_stream playback;  /* 描述playback的能力 */  

27.  };  

/*

 * Digital Audio Interface Driver.

 *

 * Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97

 * operations and capabilities. Codec and platform drivers will register this

 * structure for every DAI they have.

 *

 * This structure covers the clocking, formating and ALSA operations for each

 * interface.

 */

struct snd_soc_dai_driver {

/* DAI description */

const char *name;  /* dai驱动名字 */

 

/* DAI driver callbacks */

int (*probe)(struct snd_soc_dai *dai);  /* dai驱动的probe函数,由snd_soc_instantiate_card回调 */

int (*remove)(struct snd_soc_dai *dai); 

int (*suspend)(struct snd_soc_dai *dai);  /* 电源管理 */

int (*resume)(struct snd_soc_dai *dai); 

 

/* ops */

const struct snd_soc_dai_ops *ops;  /* 指向本daisnd_soc_dai_ops结构 */

 

/* DAI capabilities */

struct snd_soc_pcm_stream capture;  /* 描述capture的能力 */

struct snd_soc_pcm_stream playback;  /* 描述playback的能力 */

};

snd_soc_dai_ops用于实现该dai的控制和参数配置:

1.      struct snd_soc_dai_ops {  

2.          /*  

3.           * DAI clocking configuration, all optional.  

4.           * Called by soc_card drivers, normally in their hw_params.  

5.           */  

6.          int (*set_sysclk)(...);  

7.          int (*set_pll)(...);  

8.          int (*set_clkdiv)(...);  

9.          /*  

10.       * DAI format configuration  

11.       * Called by soc_card drivers, normally in their hw_params.  

12.       */  

13.      int (*set_fmt)(...);  

14.      int (*set_tdm_slot)(...);  

15.      int (*set_channel_map)(...);  

16.      int (*set_tristate)(...);  

17.      /*  

18.       * DAI digital mute - optional.  

19.       * Called by soc-core to minimise any pops.  

20.       */  

21.      int (*digital_mute)(...);  

22.      /*  

23.       * ALSA PCM audio operations - all optional.  

24.       * Called by soc-core during audio PCM operations.  

25.       */  

26.      int (*startup)(...);  

27.      void (*shutdown)(...);  

28.      int (*hw_params)(...);  

29.      int (*hw_free)(...);  

30.      int (*prepare)(...);  

31.      int (*trigger)(...);  

32.      /*  

33.       * For hardware based FIFO caused delay reporting.  

34.       * Optional.  

35.       */  

36.      snd_pcm_sframes_t (*delay)(...);  

37.  };  

 

先贴出来我们自己的snd_soc_dai_driversnd_soc_codec_driv都是怎么配置的

 

snd_soc_codec_driver

 

static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {

      .probe =   wm9713_soc_probe,

      .remove =     wm9713_soc_remove,

 

#ifdef CONFIG_PM

      .suspend =     wm9713_soc_suspend,

      .resume =     wm9713_soc_resume,

#endif

      .read = ac97_read,

      .write = ac97_write,

      .set_bias_level = wm9713_set_bias_level,

      .reg_cache_size = ARRAY_SIZE(wm9713_reg),

      .reg_word_size = sizeof(u16),

      .reg_cache_step = 2,

      .reg_cache_default = wm9713_reg,

      .dapm_widgets = wm9713_dapm_widgets,

      .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),

      .dapm_routes = wm9713_audio_map,

      .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),

};

 

struct snd_soc_dai_driver

 

static struct snd_soc_dai_driver wm9713_dai[] = {

{

      .name = "wm9713-hifi",

      .ac97_control = 1,

      .playback = {

           .stream_name = "HiFi Playback",

           .channels_min = 1,

           .channels_max = 2,

           .rates = WM9713_RATES,

           .formats = SND_SOC_STD_AC97_FMTS,},

      .capture = {

           .stream_name = "HiFi Capture",

           .channels_min = 1,

           .channels_max = 2,

           .rates = WM9713_RATES,

           .formats = SND_SOC_STD_AC97_FMTS,},

      .ops = &wm9713_dai_ops_hifi,

      },

      ……

};

 

struct snd_soc_dai_ops

 

static struct snd_soc_dai_ops wm9713_dai_ops_hifi = {

      .prepare   = ac97_hifi_prepare,

      .set_clkdiv      = wm9713_set_dai_clkdiv,

      .set_pll     = wm9713_set_dai_pll,

};

下面分析snd_soc_register_codec

 

snd_soc_register_codec首先分配给snd_soc_codec结构snd_soc_codec分配内存实例化,

codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);

确定codec的名字,名字很重要以后调用soc_probe_dai_link时候将绑定相应的codec,上文第二章已经提到

codec->name = fmt_single_name(dev, &codec->id);

然后根据传进来的snd_soc_codec_driver填充snd_soc_codec结构,紧接着进行相应的初始化配置,完成这一步后,就可以使用APIsnd_soc_read()snd_soc_write()codec的寄存器进行读写了。

 

           codec->write = codec_drv->write;

           codec->read = codec_drv->read;

           codec->volatile_register = codec_drv->volatile_register;

           codec->readable_register = codec_drv->readable_register;

           codec->writable_register = codec_drv->writable_register;

           codec->dapm.bias_level = SND_SOC_BIAS_OFF;

           codec->dapm.dev = dev;

           codec->dapm.codec = codec;

           codec->dapm.seq_notifier = codec_drv->seq_notifier;

           codec->dev = dev;

           codec->driver = codec_drv;

           codec->num_dai = num_dai

进行一些寄存器缓存的初始化和配置

           if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {

           reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;

           codec->reg_size = reg_size;

           /* it is necessary to make a copy of the default register cache

            * because in the case of using a compression type that requires

            * the default register cache to be marked as __devinitconst the

            * kernel might have freed the array by the time we initialize

            * the cache.

            */

           if (codec_drv->reg_cache_default) {

                 codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,

                                        reg_size, GFP_KERNEL);

                 if (!codec->reg_def_copy) {

                      ret = -ENOMEM;

                      goto fail;

                 }

           }

      }

根据我们配置的snd_soc_dai_driverplaybackcapture内的farmats配置找到数组codec_format_map中相应的配置

      for (i = 0; i < num_dai; i++) {

           fixup_codec_formats(&dai_drv[i].playback);

           fixup_codec_formats(&dai_drv[i].capture);

      }

之后进行一次codec dai 注册

snd_soc_register_dais(dev, dai_drv, num_dai);

snd_soc_register_daissnd_soc_register_codec基本相似也是先实例snd_soc_dai结构,初始化,最后添加全局变量链表dai_listlist_add(&dai->list, &dai_list);)这里插入一句话其实大家不难发现codec daicpu dai都在一个链表(dai_list)里而cpu dai 在哪里注册呢,cpu dai 在文件linux-3.0-rc6\sound\soc\samsung\ac97.c中注册后继具体说明

dai的注册

 

snd_soc_register_codec然后要填充全局变量链表codec_list

list_add(&codec->list, &codec_list);

最后调用snd_soc_instantiate_cards触发Machine驱动进行一次匹配绑定操作。

至此alsa框架的codec协议相关我们终于理顺了,下面我们可以看到我们驱动的主体函数,这里我们看到snd_soc_codec_driver结构里有几个重要的驱动相关的函数,这也是我们要实现的。

 

 

2. codec 初始化

上文提到当machine调用snd_soc_instantiate_cards()进行daicodecplatform匹配的时候,一旦匹配成功则会调用codecdai驱动相应probe函数,而在daicodecplatform注册的时候也都调用snd_soc_instantiate_cards()进行匹配。

而本codecprobe函数正是wm9713_soc_probe函数。

wm9713_soc_probe很简单主要做了向设备添加ac97底层接口操作函数集soc_ac97_ops和控制部件(注:这里的wm9713_soc_probe参数类似于platform设备——驱动模型,他们的设备和驱动公用同一个结构,也就是说明wm9713_soc_probecodec里类似于全局变量)。详细如下:

申请一个wm8994_priv私有数据结构,并把它设为codec设备的driver_data

struct wm9713_priv *wm9713;

……

     wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);

     ……

     snd_soc_codec_set_drvdata(codec, wm9713);

向设备添加ac97底层接口操作函数集soc_ac97_opslinux-3.0-rc6\sound\soc\samsung ac97.c后边将提到。);

ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);

设置合适的偏置电平;

添加必要的control部件;

snd_soc_add_controls(codec, wm9713_snd_ac97_controls,

                      ARRAY_SIZE(wm9713_snd_ac97_controls));

至此,codec驱动的初始化完成。

讲到这里,大家可能有那么一点点小迷糊和疑问,可能会问到我们之前在soc_codec_dev_wm9713配置时候指定的函数

.read = ac97_read,

.write = ac97_write,

……

和刚刚提到的ac97底层接口操作函数集soc_ac97_ops是怎么注册进来的,或者初始化操作是在哪里发生的呢?下文将一一理顺。

总结一下codec部分的相关工作,本质就是填充codec_list,实现上文(第一章.基础准备)提到的alsa数据结构表中的codec部分。如下图

Codec

Codec_dai

注意这里的dai指的是codec dai与之区别的还有cpu_dai必须明确这一点

 

 

3. 声卡的通信接口

wm9713有两大接口,一是AC97,另一是PCM/I2S tq2440底板原理图,不难发现WM9713PCM/I2S的接口没接线,故wm9713PCM|I2S没用上, wm9713采用ac97接口。

而市场上有一些芯片不采用ac97实现,比如WM1811WM8994WM8958,这些芯片本身具备多种功能,除了codec外,它还有作为LDOGPIO使用,这几种功能共享一些IO和中断资源,linux为这种设备提供了一套标准的实现方法:mfd设备,其基本思想是为这些功能的公共部分实现一个父设备,以便共享某些系统资源和功能,然后每个子功能实现为它的子设备,这样既共享了资源和代码,又能实现合理的设备层次结构,主要利用到的API就是:mfd_add_devices()mfd_remove_devices()mfd_cell_enable()mfd_cell_disable()mfd_clone_cell()。为了以后学习这里只是提一下mfd设备,因为wm9713没有使用mfd设备,这里不详述,有兴趣可以参考相关文章。

我上一个开发板是tq2440,它的声卡是l3接口用做控制,i2s声音的标准接口用作传输声音数据。由于wm9713ac97通信接口,同时实现控制和声音数据通信。个人认为这就是没有采用mfd设备的原因。下面进入主题。

1. 有关AC97 SpecWM9713的时钟关系

 

24.576MHz外部晶振给WM9713供给MCLKA。再由WM9713于内部供给AC97 CLK:24.576MHz,此处于WM9713 Reg44h可配置 

然后AC97 CLK:24.576MHz再供给BITCLK:12.288MHz,这个BITCLK再由WM9713提供给S3C6410用,6410再利用BITCLK:12.288MHz去产生SYNC:48KHz,这个48KHzSYNC将反过来再供给WM9713芯片及6410AC97 Controller,以使两者同步 
Refer: WM9713 Spec P22 
  
另外只要24.576MHz的外部晶振一定,供给WM9713MCLKAWM9713自动会将其分频至12.288MHzBITCLK,送往6410 AC97 Controller 

12.288MHz
BITCLK产生48KHzSYNC的机理
Refer: 6410
手册35-5: 
BITCLK:12.288MHz 
SYNC:48KHz=12.288MHz/256bit 
一个AC97帧由13Slot组成,第1 NO.0 Slot16bit组成Tag Phase 
2--13个即:NO.1--12 Slot:每个Slot 20bit,组成Data Phase 
Total Bits: 16bit+12*20=256bit 

2.ac97总线设备驱动

1)、samsung-ac97设备

上面提到关于codec ac97_read,ac97_write等跟ac97总线函数是这么来的呢。

下面通过tq6410板级设备初始化结构数组tq6410_deviceslinux-3.0-rc6\arch\arm\mach-s3c64xx\mach-tq6410.c)配置

#ifdef CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713

     &samsung_asoc_dma,

     &s3c64xx_device_ac97,

&s3c64xx_device_ac97可知,设备在板级的时候就注册了s3c64xx_device_ac97设备,我们看一下s3c64xx_device_ac97都做了什么。

 

s3c64xx_device_ac97

struct platform_device s3c64xx_device_ac97 = {

     .name         = "samsung-ac97",

     .id         = -1,

     .num_resources    = ARRAY_SIZE(s3c64xx_ac97_resource),

     .resource    = s3c64xx_ac97_resource,

     .dev = {

           .platform_data = &s3c_ac97_pdata,

           .dma_mask = &s3c64xx_ac97_dmamask,

           .coherent_dma_mask = DMA_BIT_MASK(32),

     },

};

s3c64xx_device_ac97设备主要定义了ac97硬件资源s3c64xx_ac97_resource和分配一组gpiodma等。

这里的重点是s3c64xx_ac97_resource

 

s3c64xx_ac97_resource

static struct resource exynos4_ac97_resource[] = {

     [0] = {

           .start   = EXYNOS4_PA_AC97,

           .end    = EXYNOS4_PA_AC97 + 0x100 - 1,

           .flags   = IORESOURCE_MEM,

     },

     [1] = {

           .start   = DMACH_AC97_PCMOUT,

           .end    = DMACH_AC97_PCMOUT,

           .flags   = IORESOURCE_DMA,

     },

     [2] = {

           .start   = DMACH_AC97_PCMIN,

           .end    = DMACH_AC97_PCMIN,

           .flags   = IORESOURCE_DMA,

     },

     [3] = {

           .start   = DMACH_AC97_MICIN,

           .end    = DMACH_AC97_MICIN,

           .flags   = IORESOURCE_DMA,

     },

     [4] = {

           .start   = IRQ_AC97,

           .end    = IRQ_AC97,

           .flags   = IORESOURCE_IRQ,

     },

};

根据他们flags名称就知道IORESOURCE_MEM这是io的地址空间范围,为驱动层ioremap准备。剩下的有ac97 in ac97 outac97 mic dmaIORESOURCE_IRQ是要用到的中断。Startend表示起始和结束范围。这里只是指出wm9713要用到的资源,注册在"samsung-ac97"驱动中完成。

2)、samsung-ac97驱动

根据平台(platform)设备驱动模型可知有名为"samsung-ac97"设备如果有与之同名的驱动与之匹配,就会调用相应的probe函数。而"samsung-ac97"设备定义在linux-3.0-rc6\sound\soc\samsung\ac97.c中。这里插一句为什么我们一定确定s3c_ac97_init函数一定运行呢,如果不运行"samsung-ac97"驱动也不会注册到platform驱动里面,我们的声卡也就无法运行,当然了这种情况是不可能的。因为我们知道如果一个函数__init修饰的话,编译器会把这个函数的指针统一编译到__init段,开机时候会自动调用。言回正传。下面分析"samsung-ac97"probe函数在本文中也就是s3c_ac97_probe都做了什么

 

static __devinit int s3c_ac97_probe(struct platform_device *pdev)

{

……

 

     ac97_pdata = pdev->dev.platform_data;

……

 

     /* Check for availability of necessary resource */

     dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);

……

 

     dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);

……

 

     dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);

……

 

     mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

……

 

     irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

……

 

     if (!request_mem_region(mem_res->start,

                      resource_size(mem_res), "ac97")) {

           dev_err(&pdev->dev, "Unable to request register region\n");

           return -EBUSY;

     }

 

     s3c_ac97_pcm_out.channel = dmatx_res->start;

     s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;

     s3c_ac97_pcm_in.channel = dmarx_res->start;

     s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;

     s3c_ac97_mic_in.channel = dmamic_res->start;

     s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;

……

 

     s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));

……

 

     s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");

……

     clk_enable(s3c_ac97.ac97_clk);

 

     if (ac97_pdata->cfg_gpio(pdev)) {

           dev_err(&pdev->dev, "Unable to configure gpio\n");

           ret = -EINVAL;

           goto err3;

     }

 

     ret = request_irq(irq_res->start, s3c_ac97_irq,

……

 

     ret = snd_soc_register_dais(&pdev->dev, s3c_ac97_dai,

                ARRAY_SIZE(s3c_ac97_dai));

……

 

     return ret;

}

先是取出"samsung-ac97"设备里的资源,

     dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);

……

根据设备里IORESOURCE_MEM内存资源申请内存

     if (!request_mem_region(mem_res->start,

                      resource_size(mem_res), "ac97")) {

           dev_err(&pdev->dev, "Unable to request register region\n");

           return -EBUSY;

     }

填充相关全局变量结构体,为实现soc_ac97_ops里的相关函数做准备。

     s3c_ac97_pcm_out.channel = dmatx_res->start;

     s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;

     s3c_ac97_pcm_in.channel = dmarx_res->start;

     s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;

     s3c_ac97_mic_in.channel = dmamic_res->start;

     s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;

根据申请的内存空间进行物理地址空间映射

s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));

使能时钟

clk_enable(s3c_ac97.ac97_clk);

申请中断

     ret = request_irq(irq_res->start, s3c_ac97_irq,

                           IRQF_DISABLED, "AC97", NULL);

最后注册cpu_dai

     ret = snd_soc_register_dais(&pdev->dev, s3c_ac97_dai,

                ARRAY_SIZE(s3c_ac97_dai));

至于驱动相关的方面大家可以参考代码树,这里不详述了。


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)

Archiver|手机版|小黑屋|天嵌 嵌入式开发社区 ( 粤ICP备11094220号 )

GMT+8, 2024-5-18 12:55 , Processed in 1.031250 second(s), 15 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

返回顶部