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

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

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

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

位于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. if (driver->controls)
  10. snd_soc_add_controls(codec, driver->controls,
  11. driver->num_controls);
  12. if (driver->dapm_routes)
  13. snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes,
  14. driver->num_dapm_routes);
  15. //...
  16. }

同时可看到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. {
  3. return snd_soc_register_codec(&pdev->dev,
  4. &soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
  5. }

随后像普通的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. {
  3. .name = "wm9713-hifi",
  4. .ac97_control = 1,
  5. .playback = {
  6. .stream_name = "HiFi Playback",
  7. .channels_min = 1,
  8. .channels_max = 2,
  9. .rates = WM9713_RATES,
  10. .formats = SND_SOC_STD_AC97_FMTS,},
  11. .capture = {
  12. .stream_name = "HiFi Capture",
  13. .channels_min = 1,
  14. .channels_max = 2,
  15. .rates = WM9713_RATES,
  16. .formats = SND_SOC_STD_AC97_FMTS,},
  17. .ops = &wm9713_dai_ops_hifi,
  18. },
  19. //...
  20. //...
  21. };

注:之前的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. {
  4. dev_set_drvdata(codec->dev, data);
  5. }
  6. static inline void *snd_soc_codec_get_drvdata(struct snd_soc_codec *codec)
  7. {
  8. return dev_get_drvdata(codec->dev);
  9. }

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

使用方法如下:

       
       
       
       
  1. static int wm9713_soc_probe(struct snd_soc_codec *codec)
  2. {
  3. struct wm9713_priv *wm9713;
  4. int ret = 0, reg;
  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. static int wm9713_soc_remove(struct snd_soc_codec *codec)
  12. {
  13. struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
  14. snd_soc_free_ac97_codec(codec);
  15. kfree(wm9713);
  16. return 0;
  17. }

设置控制接口-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. {
  24. int i;
  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. codec->write = io_types[i].write;
  36. codec->read = hw_read;
  37. codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
  38. switch (control) {
  39. case SND_SOC_I2C:
  40. #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
  41. codec->hw_write = (hw_write_t)i2c_master_send;
  42. #endif
  43. if (io_types[i].i2c_read)
  44. codec->hw_read = io_types[i].i2c_read;
  45. codec->control_data = container_of(codec->dev,
  46. struct i2c_client,
  47. dev);
  48. break;
  49. case SND_SOC_SPI:
  50. #ifdef CONFIG_SPI_MASTER
  51. codec->hw_write = do_spi_write;
  52. #endif
  53. if (io_types[i].spi_read)
  54. codec->hw_read = io_types[i].spi_read;
  55. codec->control_data = container_of(codec->dev,
  56. struct spi_device,
  57. dev);
  58. break;
  59. }
  60. return 0;
  61. }
  62. EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);

你可能感兴趣的:(linux,cache,struct,Codec,linux内核,Types)