织梦CMS - 轻松建站从此开始!

罗索

当前位置: 主页>嵌入式开发>Android>

ALSA SOC在Linux3.1上的一些改进

落鹤生 发布于 2012-03-20 15:05 点击:次 
Android迈进了4.0,相应的Linux内核也进入了3.x时代。之后的一个工作估计要将2.6.32的驱动移植到3.x上面来。因此趁现在有空,看看alsa在这方面有什么改动。
TAG:

2012已经到来,无论这个世界是否行将毁灭,在那之前的日子还得要继续。

Android迈进了4.0,相应的Linux内核也进入了3.x时代。之后的一个工作估计要将2.6.32的驱动移植到3.x上面来。因此趁现在有空,看看alsa在这方面有什么改动。

总的来说,架构大的改动是不大可能的。codec中几个关键结构体没有大的变化,如snd_soc_dai_ops、 snd_soc_dai_driver(相当于2.6.32中的snd_soc_dai),倒是以前的snd_soc_codec_device重定义为 snd_soc_codec_driver,这个算是最明显的。

注册用结构体-snd_soc_codec_driver

2.6.32:

  1. /* codec device */ 
  2. struct snd_soc_codec_device { 
  3.     int (*probe)(struct platform_device *pdev); 
  4.     int (*remove)(struct platform_device *pdev); 
  5.     int (*suspend)(struct platform_device *pdev, pm_message_t state); 
  6.     int (*resume)(struct platform_device *pdev); 
  7. }; 

3.1.1:

  1. /* codec driver */ 
  2. struct snd_soc_codec_driver { 
  3.  
  4.     /* driver ops */ 
  5.     int (*probe)(struct snd_soc_codec *); 
  6.     int (*remove)(struct snd_soc_codec *); 
  7.     int (*suspend)(struct snd_soc_codec *, 
  8.             pm_message_t state); 
  9.     int (*resume)(struct snd_soc_codec *); 
  10.  
  11.     /* Default control and setup, added after probe() is run */ 
  12.     const struct snd_kcontrol_new *controls; 
  13.     int num_controls; 
  14.     const struct snd_soc_dapm_widget *dapm_widgets; 
  15.     int num_dapm_widgets; 
  16.     const struct snd_soc_dapm_route *dapm_routes; 
  17.     int num_dapm_routes; 
  18.  
  19.     /* codec wide operations */ 
  20.     int (*set_sysclk)(struct snd_soc_codec *codec, 
  21.               int clk_id, unsigned int freq, int dir); 
  22.     int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source, 
  23.         unsigned int freq_in, unsigned int freq_out); 
  24.  
  25.     /* codec IO */ 
  26.     unsigned int (*read)(struct snd_soc_codec *, unsigned int); 
  27.     int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); 
  28.     int (*display_register)(struct snd_soc_codec *, char *, 
  29.                 size_t, unsigned int); 
  30.     int (*volatile_register)(struct snd_soc_codec *, unsigned int); 
  31.     int (*readable_register)(struct snd_soc_codec *, unsigned int); 
  32.     int (*writable_register)(struct snd_soc_codec *, unsigned int); 
  33.     short reg_cache_size; 
  34.     short reg_cache_step; 
  35.     short reg_word_size; 
  36.     const void *reg_cache_default; 
  37.     short reg_access_size; 
  38.     const struct snd_soc_reg_access *reg_access_default; 
  39.     enum snd_soc_compress_type compress_type; 
  40.  
  41.     /* codec bias level */ 
  42.     int (*set_bias_level)(struct snd_soc_codec *, 
  43.                   enum snd_soc_bias_level level); 
  44.  
  45.     void (*seq_notifier)(struct snd_soc_dapm_context *, 
  46.                  enum snd_soc_dapm_type, int); 
  47.  
  48.     /* probe ordering - for components with runtime dependencies */ 
  49.     int probe_order; 
  50.     int remove_order; 
  51. }; 

位于snd_soc_codec_driver中的一些codec IO成员函数和set_bias_level回调函数原来都放在另外一个结构体snd_soc_codec中,现在放置在这里了,这是根据源码结构调整的结果。事实真正需要设置的成员也不是很多,如下:

  1. static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { 
  2.     .probe =    wm9713_soc_probe, 
  3.     .remove =   wm9713_soc_remove, 
  4.     .suspend =  wm9713_soc_suspend, 
  5.     .resume =   wm9713_soc_resume, 
  6.     .read = ac97_read, 
  7.     .write = ac97_write, 
  8.     .set_bias_level = wm9713_set_bias_level, 
  9.     .reg_cache_size = ARRAY_SIZE(wm9713_reg), 
  10.     .reg_word_size = sizeof(u16), 
  11.     .reg_cache_step = 2, 
  12.     .reg_cache_default = wm9713_reg, 
  13.     .dapm_widgets = wm9713_dapm_widgets, 
  14.     .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets), 
  15.     .dapm_routes = wm9713_audio_map, 
  16.     .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map), 
  17. }; 

probe、remove、suspend、resume相信不用累述了,volatile_register函数判断指定的寄存器是否 volatile,reg_cache_size一般为寄存器数目,reg_word_size为寄存器的字长,reg_cache_default为寄 存器默认值配置表。

而dapm_widgets、dapm_routes就比较“犀利”了。之前的dapm widgets和routes分别通过函数snd_soc_dapm_new_controls和snd_soc_dapm_add_routes来注册 的(当然现在还保留这些接口),现在则可以填入到这个结构体,在soc-core里注册,省了不少功夫:

  1. static int soc_probe_codec(struct snd_soc_card *card, 
  2.                struct snd_soc_codec *codec) 
  3.     //... 
  4.  
  5.     if (driver->dapm_widgets) 
  6.         snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, 
  7.                       driver->num_dapm_widgets); 
  8.  
  9.     //... 
  10.  
  11.     if (driver->controls) 
  12.         snd_soc_add_controls(codec, driver->controls, 
  13.                      driver->num_controls); 
  14.     if (driver->dapm_routes) 
  15.         snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, 
  16.                     driver->num_dapm_routes); 
  17.     //... 

同时可看到dirver ops的函数参数有所不同了,以前是struct platform_device *pdev,现在改为struct snd_soc_codec *codec,这个与codec的注册函数snd_soc_register_codec和设备的drvdata有关,之后会逐一分析。

注册函数-snd_soc_register_codec

对于snd_soc_codec_driver,之前需要EXPORT_SYMBOL_GPL(soc_codec_dev_wm9713),然后在其他地方注册,现在不用那么麻烦了:

  1. static __devinit int wm9713_probe(struct platform_device *pdev) 
  2.     return snd_soc_register_codec(&pdev->dev, 
  3.             &soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai)); 

随后像普通的platform设备那样注册就行了。不再需要snd_soc_new_pcms注册pcm、snd_soc_init_card注册card。

snd_soc_register_codec函数原型:

  1. int snd_soc_register_codec(struct device *dev, 
  2.         const struct snd_soc_codec_driver *codec_drv, 
  3.         struct snd_soc_dai_driver *dai_drv, int num_dai); 

其中snd_soc_dai_driver与之前的snd_soc_dai类似,没有特别需要注意的地方,初始化如:

  1. static struct snd_soc_dai_driver wm9713_dai[] = { 
  2.     .name = "wm9713-hifi"
  3.     .ac97_control = 1, 
  4.     .playback = { 
  5.         .stream_name = "HiFi Playback"
  6.         .channels_min = 1, 
  7.         .channels_max = 2, 
  8.         .rates = WM9713_RATES, 
  9.         .formats = SND_SOC_STD_AC97_FMTS,}, 
  10.     .capture = { 
  11.         .stream_name = "HiFi Capture"
  12.         .channels_min = 1, 
  13.         .channels_max = 2, 
  14.         .rates = WM9713_RATES, 
  15.         .formats = SND_SOC_STD_AC97_FMTS,}, 
  16.     .ops = &wm9713_dai_ops_hifi, 
  17.     }, 
  18.     //... 
  19.     //... 
  20. }; 

注:之前的snd_soc_dai也需要EXPORT_SYMBOL_GPL的,然后在其他地方注册的,现在改进了这点,减少了export的symbol,代码架构更加清晰可靠。

设备私有数据-drvdata

ALSA之CODEC分析中 有提到“开始看到socdev = platform_get_drvdata(pdev)这句不免有点疑惑,到底pdev是在哪里初始化好了?”,这个pdev就是drvdata。这个东 东非常重要,它一般包含codec自定义的私有数据,如控制接口类型(I2C/SPI/AC97)、fll_in(FLL input frequency)、fll_out(FLL output frequency)等信息。因为每种codec可能定义的私有数据体不同,而Linux内核喜欢抽象,所以就产生了drvdata。

之前drvdata的来龙去脉是百转千回绕吊瓶的,而现在版本是比较直观的,且看分析:

  1. static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec, 
  2.         void *data) 
  3.     dev_set_drvdata(codec->dev, data); 
  4.  
  5. static inline void *snd_soc_codec_get_drvdata(struct snd_soc_codec *codec) 
  6.     return dev_get_drvdata(codec->dev); 

soc.h里实现两个函数用于设置和获取drvdata。snd_soc_codec是codec驱动里最常使用的结构体,在各个操作函数均可以看到它的身影,所以选择它和drvdata联系起来。

使用方法如下:

  1. static int wm9713_soc_probe(struct snd_soc_codec *codec) 
  2.     struct wm9713_priv *wm9713; 
  3.     int ret = 0, reg; 
  4.  
  5.     wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL); 
  6.     if (wm9713 == NULL) 
  7.         return -ENOMEM; 
  8.     snd_soc_codec_set_drvdata(codec, wm9713); 
  9.      
  10.     //... 
  11.  
  12. static int wm9713_soc_remove(struct snd_soc_codec *codec) 
  13.     struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); 
  14.     snd_soc_free_ac97_codec(codec); 
  15.     kfree(wm9713); 
  16.     return 0; 

设置控制接口-snd_soc_codec_set_cache_io

这个函数在以前版本也有的,但是当时没有留意。现在发现这个函数很实用,只需要配置寄存器地址宽度、数据宽度、和控制接口类型,soc-io模块就自动会选择合适的控制接口函数。

  1. /** 
  2.  * snd_soc_codec_set_cache_io: Set up standard I/O functions. 
  3.  * 
  4.  * @codec: CODEC to configure. 
  5.  * @addr_bits: Number of bits of register address data. 
  6.  * @data_bits: Number of bits of data per register. 
  7.  * @control: Control bus used. 
  8.  * 
  9.  * Register formats are frequently shared between many I2C and SPI 
  10.  * devices.  In order to promote code reuse the ASoC core provides 
  11.  * some standard implementations of CODEC read and write operations 
  12.  * which can be set up using this function. 
  13.  * 
  14.  * The caller is responsible for allocating and initialising the 
  15.  * actual cache. 
  16.  * 
  17.  * Note that at present this code cannot be used by CODECs with 
  18.  * volatile registers. 
  19.  */ 
  20. int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, 
  21.                    int addr_bits, int data_bits, 
  22.                    enum snd_soc_control_type control) 
  23.     int i; 
  24.  
  25.     for (i = 0; i < ARRAY_SIZE(io_types); i++) 
  26.         if (io_types[i].addr_bits == addr_bits && 
  27.             io_types[i].data_bits == data_bits) 
  28.             break
  29.     if (i == ARRAY_SIZE(io_types)) { 
  30.         printk(KERN_ERR 
  31.                "No I/O functions for %d bit address %d bit data\n"
  32.                addr_bits, data_bits); 
  33.         return -EINVAL; 
  34.     } 
  35.  
  36.     codec->write = io_types[i].write; 
  37.     codec->read = hw_read; 
  38.     codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; 
  39.  
  40.     switch (control) { 
  41.     case SND_SOC_I2C: 
  42. #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) 
  43.         codec->hw_write = (hw_write_t)i2c_master_send; 
  44. #endif 
  45.         if (io_types[i].i2c_read) 
  46.             codec->hw_read = io_types[i].i2c_read; 
  47.  
  48.         codec->control_data = container_of(codec->dev, 
  49.                            struct i2c_client, 
  50.                            dev); 
  51.         break
  52.  
  53.     case SND_SOC_SPI: 
  54. #ifdef CONFIG_SPI_MASTER 
  55.         codec->hw_write = do_spi_write; 
  56. #endif 
  57.         if (io_types[i].spi_read) 
  58.             codec->hw_read = io_types[i].spi_read; 
  59.  
  60.         codec->control_data = container_of(codec->dev, 
  61.                            struct spi_device, 
  62.                            dev); 
  63.         break
  64.     } 
  65.  
  66.     return 0; 
  67. EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); 

 

(sepnic)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201203/15847.html]
本文出处:CSDN博客 作者:sepnic 原文
顶一下
(4)
80%
踩一下
(1)
20%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容