zoukankan      html  css  js  c++  java
  • ASOC 音频子系统框架

    基于: Mini2440 开发板, Linux 3.4.2 内核

    ASOC 简介:

    ASoC - ALSA System on Chip,是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中音频 Codec 的一套软件体系。
    
    就像软件领域里的抽象和重用一样,嵌入式设备的音频系统可以被划分为 Machine、Platform、 Codec。
    

    所以,我们可以把 ASOC 的驱动分为 3 个部分,以内核中的文件为例:

    Machine:
        S3c24xx_uda134x.c (soundsocsamsung)
        
    Platform:
        S3c24xx-i2s.c (soundsocsamsung)
        Dma.c (soundsocsamsung)
    
    Codec:
        Uda134x.c (soundsoccodecs)
    

    我们以 S3c24xx_uda134x.c 文件中的 s3c24xx_uda134x_probe 函数为起点:

    /* 创建并配置一个 snd_soc_card 结构体 */
    static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
    	.name = "S3C24XX_UDA134X",
    	.owner = THIS_MODULE,
    	.dai_link = &s3c24xx_uda134x_dai_link,
    	.num_links = 1,
    };
    
    /* 申请一个名为 "soc-audio" 的 platform_device */
    s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
    
    /* 设置 platform_device 的 drvdata */
    platform_set_drvdata(s3c24xx_uda134x_snd_device,
    		     &snd_soc_s3c24xx_uda134x);
    		     
    /* 将 s3c24xx_uda134x 添加进平台设备,它是 UDA1341 芯片的引脚状态信息 */
    platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
    
    /* 添加平台设备 */
    ret = platform_device_add(s3c24xx_uda134x_snd_device);
    

    这里的 snd_soc_dai_link 结构尤为重要,在它的内部指定了 Platform、Codec、 codec_dai、 cpu_dai 的名字,稍后 Machine 驱动将会利用这些名字去匹配已经在系统中注册了的 platform、codec、dai,这些注册的接口都是在另外相应的 Platform 驱动和
    Codec 驱动的代码文件中定义的。由此可见,Machine 的功能可认为是匹配相应的驱动实现一个完成的音频控制体系。

    注册了名为 "soc-audio" 的 platform_device 之后,系统会在内核中找到同名的 driver,随后调用 driver 的 probe 函数,该函数主要是完成声卡的实例化、调用各个子接口的 probe 函数、创建标准 alsa 驱动的 pcm 逻辑设备以及完成声卡的注册。

    Uda134x.c 文件

    该文件为 ASOC 的 Codec 驱动,主要是完成 Codec 芯片的各种操控,我们以 uda134x_codec_probe 函数为入口:

    static struct snd_soc_dai_driver uda134x_dai = {
    	.name = "uda134x-hifi",
    	/* playback capabilities */
    	.playback = {
    		.stream_name = "Playback",
    		.channels_min = 1,
    		.channels_max = 2,
    		.rates = UDA134X_RATES,
    		.formats = UDA134X_FORMATS,
    	},
    	/* capture capabilities */
    	.capture = {
    		.stream_name = "Capture",
    		.channels_min = 1,
    		.channels_max = 2,
    		.rates = UDA134X_RATES,
    		.formats = UDA134X_FORMATS,
    	},
    	/* pcm operations */
    	.ops = &uda134x_dai_ops,
    };
    
    static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
    	.probe =        uda134x_soc_probe,
    	.remove =       uda134x_soc_remove,
    	.suspend =      uda134x_soc_suspend,
    	.resume =       uda134x_soc_resume,
    	.reg_cache_size = sizeof(uda134x_reg),
    	.reg_word_size = sizeof(u8),
    	.reg_cache_default = uda134x_reg,
    	.reg_cache_step = 1,
    	.read = uda134x_read_reg_cache,
    	.write = uda134x_write,
    	.set_bias_level = uda134x_set_bias_level,
    };
    
    /* 注册 Codec */
    snd_soc_register_codec(&pdev->dev, &soc_codec_dev_uda134x, &uda134x_dai, 1);
    

    snd_soc_register_codec 函数的大体步骤:

    /* 分配 snd_soc_codec */
    1. codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
    
    2. 配置 Codec 
    
    /* 注册 dais */
    3. snd_soc_register_dais:
    
        /* 分配 snd_soc_dai */
        1. dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
        
        2. 配置 Dai
        
        3. 将 Dai 加入到 dai_list
        
        4. 调用 snd_soc_instantiate_cards 函数触发一次匹配绑定操作
    
    4. 将 Codec 加入到 codec_list
    
    5. 调用 snd_soc_instantiate_cards 函数触发一次匹配绑定操作
    

    S3c24xx-i2s.c 与 Dma.c 文件

    ASOC 把 Platform 驱动分为两个部分:snd_soc_platform_driver 和
    snd_soc_dai_driver。其中,platform_driver 负责管理音频数据,把音频数据通过 dma 或其他操作传送至 cpu dai
    中,dai_driver 则主要完成 cpu 一侧的 dai 的参数配置,同时也会通过一定的途径把必要的 dma 等参数与 snd_soc_platform_driver 进行交互。

    先来看 Dma.c 中的 samsung_asoc_platform_probe 函数:

    static struct snd_pcm_ops dma_ops = {
    	.open		= dma_open,
    	.close		= dma_close,
    	.ioctl		= snd_pcm_lib_ioctl,
    	.hw_params	= dma_hw_params,
    	.hw_free	= dma_hw_free,
    	.prepare	= dma_prepare,
    	.trigger	= dma_trigger,
    	.pointer	= dma_pointer,
    	.mmap		= dma_mmap,
    };
    
    static struct snd_soc_platform_driver samsung_asoc_platform = {
    	.ops		= &dma_ops,
    	.pcm_new	= dma_new,
    	.pcm_free	= dma_free_dma_buffers,
    };
    
    /* 注册 soc platform */
    snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
    

    snd_soc_register_platform 函数中申请、配置了 snd_soc_platform 结构,并将其加入到了 platform_list,然后调用 snd_soc_instantiate_cards 函数进行绑定。

    samsung_asoc_platform 结构中的 pcm_new 主要用来分配一个 buffer,dma 使用它来操作音频数据; pcm_free 用来释放这个 buffer; ops 中为涉及到 dma 操作以及 dma buffer 管理等工作的函数。

    S3c24xx-i2s.c 中的 s3c24xx_iis_dev_probe 函数:

    static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
    	.trigger	= s3c24xx_i2s_trigger,
    	.hw_params	= s3c24xx_i2s_hw_params,
    	.set_fmt	= s3c24xx_i2s_set_fmt,
    	.set_clkdiv	= s3c24xx_i2s_set_clkdiv,
    	.set_sysclk	= s3c24xx_i2s_set_sysclk,
    };
    
    static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
    	.probe = s3c24xx_i2s_probe,
    	.suspend = s3c24xx_i2s_suspend,
    	.resume = s3c24xx_i2s_resume,
    	.playback = {
    		.channels_min = 2,
    		.channels_max = 2,
    		.rates = S3C24XX_I2S_RATES,
    		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
    	.capture = {
    		.channels_min = 2,
    		.channels_max = 2,
    		.rates = S3C24XX_I2S_RATES,
    		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
    	.ops = &s3c24xx_i2s_dai_ops,
    };
    
    /* 注册 soc dai */
    snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);
    

    snd_soc_register_dai 函数中申请、配置了 snd_soc_dai 结构,并将其加入到了 dai_list,然后调用 snd_soc_instantiate_cards 函数进行绑定。

    这两个结构中的函数不难理解,主要是为了配置 I2S 的引脚及时钟、dai 数据格式等。

    这些函数可能会由 soc-core 或 Machine 驱动中的函数调用。

  • 相关阅读:
    P2515 [HAOI2010]软件安装
    P2502 [HAOI2006]旅行 最小生成树
    Luogu P2511 [HAOI2008]木棍分割 二分+DP
    P2303 [SDOI2012]Longge的问题 我傻QwQ
    P1129 [ZJOI2007]矩阵游戏 二分图匹配
    BZOJ3211花神游历各国
    [HNOI2011]XOR和路径
    NOIP考前划水
    [IOI2018]组合动作
    [NOIP2017]逛公园
  • 原文地址:https://www.cnblogs.com/GyForever1004/p/8619115.html
Copyright © 2011-2022 走看看