• ASOC驱动框架分析


        ASOC(ALSA system on chip)。是由wolfson公司做的一个针对嵌入式移动设备的声音驱动的架构。支持三种不同的CODEC接口:AC97(Intel),I2S(Philips),PCM接口。ASOC在实现上区分了具体的平台和CODEC,从而使得同一个CODEC芯片可以在不同的体系结构中使用。而相关的与体系统结构相关的部分主要实现DMA的传输和控制管理。

      所谓ALSA,(Advansed Linux Sound Architecture,高级Linux声音架构),是Linux 2.6 版本内核的声音子系统。

      2.4版本的声音子系统叫OSS(Open Sound System,开放声音系统),现以去除,不过为了兼容,ALSA会提供OSS的接口(设备节点)。

     

      本驱动框架分析基于mini2440开发板,其中配备了片上系统s3c2440,声卡Uda1341。

      分析包括的文件有:

           arch\arm\mach-s3c2440\Mach-mini2440.c  (平台设备)

          sound\soc\s3c24xx\S3c24xx_uda134x.c  (平台驱动)

          sound\soc\Soc-core.c            (ALSA主模块)

          sound\soc\codecs\Uda134x.c        (声音控制)

          sound\soc\s3c24xx\S3c24xx-i2s.c      (i2s初始化)

          sound\soc\s3c24xx\S3c24xx-pcm.c       (音频码流传输)

     

    arch\arm\mach-s3c2440\Mach-mini2440.c

    文件设置了各种各样的mini2440平台设备,当我们需要注册平台驱动的时候,会去比对该文件中设备的名称跟我们自己驱动的设备名称,发现同名设备则调用驱动内的probe函数。

    View Code
        static struct platform_device s3c24xx_uda134x = {
            .name = "s3c24xx_uda134x",
            .dev = {
                .platform_data    = &s3c24xx_uda134x_data,
            }
        };
        
        static struct s3c24xx_uda134x_platform_data s3c24xx_uda134x_data = {
            .l3_clk = S3C2410_GPB(4),
            .l3_data = S3C2410_GPB(3),
            .l3_mode = S3C2410_GPB(2),
            .model = UDA134X_UDA1341,
        };

    sound\soc\s3c24xx\S3c24xx_uda134x.c

    设置了跟Mach-mini2440.c中同名的驱动"s3c24xx_uda134x"

    View Code
    S3c24xx_uda134x.c    //平台驱动
        static struct platform_driver s3c24xx_uda134x_driver = {
            .probe  = s3c24xx_uda134x_probe,
            .remove = s3c24xx_uda134x_remove,
            .driver = {
                .name = "s3c24xx_uda134x",
                .owner = THIS_MODULE,
            },
        };

     

    发现同名设备后调用probe函数

    probe函数内部创建了名为“soc-audio”的声卡设备,然后用s3c24xx_uda134x_snd_devdata设置设备信息

    View Code
    s3c24xx_uda134x_probe(struct platform_device *pdev)
            s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,"data")    //初始化l3所用到的gpio
            s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,"clk")
            s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,"mode")
    
            s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);    //创建声卡dev(名字为soc-audio)
            platform_set_drvdata(s3c24xx_uda134x_snd_device,&s3c24xx_uda134x_snd_devdata);
            platform_device_add(s3c24xx_uda134x_snd_device);

     

    s3c24xx_uda134x_snd_devdata是对整个声卡结构体的封装

    View Code
    static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
        .card = &snd_soc_s3c24xx_uda134x,
        .codec_dev = &soc_codec_dev_uda134x,
        .codec_data = &s3c24xx_uda134x,
    };

    其中的snd_soc_s3c24xx_uda134x联系起声卡驱动各个模块

    View Code
    static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
            .name = "S3C24XX_UDA134X",
            .platform = &s3c24xx_soc_platform,        //S3c24xx-pcm.c,向用户空间提供pcm操作函数
            .dai_link = &s3c24xx_uda134x_dai_link,        //关联codec(uda134x)跟cpu(s3c24xx)
            .num_links = 1,
        };

    dai(digital audio interface,数字音频接口),dai_link包含了两个dai接口:uda134x_dai 跟 s3c24xx_i2s_dai

    View Code
        static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
            .name = "UDA134X",
            .stream_name = "UDA134X",
            .codec_dai = &uda134x_dai,        //Uda134x.c,创建声卡设备
            .cpu_dai = &s3c24xx_i2s_dai,        //S3c24xx-i2s.c,初始化i2s
            .ops = &s3c24xx_uda134x_ops,
        };

     

    sound\soc\Soc-core.c

    开始创建声卡驱动,作为总线设备驱动框架下的一员,在发现同名设备“soc-audio”后,soc_driver中的probe函数会被调用

    View Code
    Soc-core.c        //声卡drv
        static struct platform_driver soc_driver = {
        .driver        = {
                .name        = "soc-audio",
                .owner        = THIS_MODULE,
                .pm        = &soc_pm_ops,
                },
            .probe        = soc_probe,
            .remove        = soc_remove,
            };

    在probe函数内部会把新增的声卡snd_soc_s3c24xx_uda134x加入card链表card_list,然后对声卡链表的每个节点,进行platform(码流传输模块),dai_list(数字音频接口)比对,如果发现相同,则调用各自的probe函数(这里card没有probe函数)

    View Code
        soc_probe(struct platform_device *pdev)
            struct snd_soc_device *socdev = platform_get_drvdata(pdev);
            struct snd_soc_card *card = socdev->card;
            ret = snd_soc_register_card(card);
                list_add(&card->list, &card_list);
                snd_soc_instantiate_cards();
                    list_for_each_entry(card, &card_list, list)
                        snd_soc_instantiate_card(card);
                            list_for_each_entry(platform, &platform_list, list)
                                if (card->platform == platform) {
                                found = 1;
                                break;
                            }
                            list_for_each_entry(dai, &dai_list, list)
                                if (card->dai_link[i].cpu_dai == dai) {
                                found = 1;
                                break;
                            }
                            for (i = 0; i < card->num_links; i++) {
                                if (!card->dai_link[i].codec_dai->ops)
                                    card->dai_link[i].codec_dai->ops = &null_dai_ops;
                            }
                            card->probe(pdev);
                            cpu_dai->probe(pdev, cpu_dai);        //s3c24xx_i2s_probe
                            codec_dev->probe(pdev);            //uda134x_soc_probe
                            platform->probe(pdev);

     

    另外,Soc-core.c中也向外提供函数,如:

    View Code
        snd_soc_init_card(struct snd_soc_device *socdev)
            snd_card_register(codec->card);                        //位于sound\core\init.c
                device_create(sound_class, card->dev,MKDEV(0, 0), card,"card%i", card->number);        //创建设备节点

     

    可以注意到上方的函数中,probe有四个,除了已经注册的一个card之外,还有另外三个

    以下三个文件就是这三个probe所在的变量的本体

     

    Uda134x.c

    提供音量控制等的声音操作函数,并且向pcm提供声音操作函数

    View Code
    uda134x_init(void)
            snd_soc_register_dai(&uda134x_dai);        /*Soc-core.c提供该函数*/
                list_add(&dai->list, &dai_list);    //uda134x_dai加入dai_list
                snd_soc_instantiate_cards();
                    list_for_each_entry(card, &card_list, list)
                        snd_soc_instantiate_card(card);
                            list_for_each_entry(platform, &platform_list, list)
                                if (card->platform == platform) {
                                found = 1;
                                break;
                            }
                            list_for_each_entry(dai, &dai_list, list)
                                if (card->dai_link[i].cpu_dai == dai) {
                                found = 1;
                                break;
                            }
                            for (i = 0; i < card->num_links; i++) {
                                if (!card->dai_link[i].codec_dai->ops)
                                    card->dai_link[i].codec_dai->ops = &null_dai_ops;
                            }
                            card->probe(pdev);
                            cpu_dai->probe(pdev, cpu_dai);        //s3c24xx_i2s_probe
                            codec_dev->probe(pdev);            //uda134x_soc_probe
                            platform->probe(pdev);
    
        struct snd_soc_dai uda134x_dai = {
            .name = "UDA134X",
            /* 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_dai_ops uda134x_dai_ops = {
            .startup    = uda134x_startup,
            .shutdown    = uda134x_shutdown,
            .hw_params    = uda134x_hw_params,
            .digital_mute    = uda134x_mute,
            .set_sysclk    = uda134x_set_dai_sysclk,
            .set_fmt    = uda134x_set_dai_fmt,
        };

    另外就是该文件提供的probe函数是最终创建出声卡实例,使声卡能够运行于操作系统之上

    View Code
        uda134x_soc_probe(struct platform_device *pdev)
            snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);        //创建声卡pcm实例
                soc_new_pcm(socdev, &card->dai_link[i], i)                //S3c-core.c提供的实例创建函数
                    snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,capture, &pcm);
                    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);    //设置操作函数
                    ret = platform->pcm_new(codec->card, codec_dai, pcm);        //platform = s3c24xx_soc_platform, pcm_new = s3c24xx_pcm_new,
                        s3c24xx_pcm_new(struct snd_card *card,struct snd_soc_dai *dai, struct snd_pcm *pcm)
                            s3c24xx_pcm_preallocate_dma_buffer(pcm,SNDRV_PCM_STREAM_PLAYBACK);    //分配DMA
            snd_soc_add_controls(codec, uda1341_snd_controls,ARRAY_SIZE(uda1341_snd_controls));    //将声卡与混频控制单元关联,控制音量
            snd_soc_init_card(socdev);            //Soc-core.c提供,提交声卡(创建设备节点)
    
        struct snd_soc_codec_device soc_codec_dev_uda134x = {
            .probe =        uda134x_soc_probe,
            .remove =       uda134x_soc_remove,
            .suspend =      uda134x_soc_suspend,
            .resume =       uda134x_soc_resume,
        };
    
        static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
            .card = &snd_soc_s3c24xx_uda134x,
            .codec_dev = &soc_codec_dev_uda134x,
            .codec_data = &s3c24xx_uda134x,
        };
        
        static const struct snd_kcontrol_new uda1340_snd_controls[] = {        //声音控制
        SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
    
        SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
        SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
    
        SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
        SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
    
        SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
        };

     

    S3c24xx-i2s.c

    进行soc的i2s端口的初始化,向pcm提供i2s操作接口,在probe函数内初始化i2s用到的GPIO

    View Code
        s3c24xx_i2s_exit(void)
            snd_soc_register_dai(&s3c24xx_i2s_dai);        /*Soc-core.c提供该函数*/
                list_add(&dai->list, &dai_list);
                snd_soc_instantiate_cards();
                    list_for_each_entry(card, &card_list, list)
                        snd_soc_instantiate_card(card);
                            list_for_each_entry(platform, &platform_list, list)
                                if (card->platform == platform) {
                                found = 1;
                                break;
                            }
                            list_for_each_entry(dai, &dai_list, list)
                                if (card->dai_link[i].cpu_dai == dai) {
                                found = 1;
                                break;
                            }
                            for (i = 0; i < card->num_links; i++) {
                                if (!card->dai_link[i].codec_dai->ops)
                                    card->dai_link[i].codec_dai->ops = &null_dai_ops;
                            }
                            card->probe(pdev);
                            cpu_dai->probe(pdev, cpu_dai);        //s3c24xx_i2s_probe
                            codec_dev->probe(pdev);            //uda134x_soc_probe
                            platform->probe(pdev);
    
        struct snd_soc_dai s3c24xx_i2s_dai = {
            .name = "s3c24xx-i2s",
            .id = 0,
            .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,
        };
    
        static 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,
        };
    
    
        s3c24xx_i2s_probe(struct platform_device *pdev,struct snd_soc_dai *dai)
            /* Configure the I2S pins in correct mode */
            s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
            s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
            s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
            s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
            s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
    
            writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
    
            s3c24xx_snd_txctrl(0);
            s3c24xx_snd_rxctrl(0);

     

     

    S3c24xx-pcm.c

    向设备节点提供pcm操作函数ioctl等,后面会被封装成设备节点的fops,没有probe函数

    View Code
        struct snd_soc_platform s3c24xx_soc_platform = {
            .name        = "s3c24xx-audio",
            .pcm_ops     = &s3c24xx_pcm_ops,
            .pcm_new    = s3c24xx_pcm_new,
            .pcm_free    = s3c24xx_pcm_free_dma_buffers,
        };
    
        static struct snd_pcm_ops s3c24xx_pcm_ops = {
            .open        = s3c24xx_pcm_open,
            .close        = s3c24xx_pcm_close,
            .ioctl        = snd_pcm_lib_ioctl,
            .hw_params    = s3c24xx_pcm_hw_params,
            .hw_free    = s3c24xx_pcm_hw_free,
            .prepare    = s3c24xx_pcm_prepare,
            .trigger    = s3c24xx_pcm_trigger,
            .pointer    = s3c24xx_pcm_pointer,
            .mmap        = s3c24xx_pcm_mmap,
        };

    总结:

  • 相关阅读:
    AVUE 根据 某个字段 倒序查询
    Java hutool工具包的使用
    AVUE 添加搜索项
    SpringBlade 添加 回收站功能
    接口 form-data 将对象转换为复杂url参数
    AVUE 隐藏 新增按钮
    AVUE 查看crud列的属性配置
    AVUE dialog对话框 去掉 点击屏幕空白区 就关闭弹框
    接口 C#/Java 请求数据 raw 的方式传输复杂对象
    接口 PostMan 常用
  • 原文地址:https://www.cnblogs.com/TaigaCon/p/2808215.html
Copyright © 2020-2023  润新知