alsa-lib -> alsa-soc -> alsa-driver -> audio hardware

APP                    Kernel              HW

ALSA-lib:向应用层提供API接口。
alsa-driver: 音频硬件设备驱动,由三大部分组成:Machine,Platform,Codec。
查看/dev/snd 文件夹中的内容:
PCM,ControlC0。PCM主要负责ADC、DAC,Control主要让用户空间的应用程序(alsa-lib)可以访问和控制codec芯片中的多路开关,滑动控件等

1.alsa_sound_init

设备树中,sound节点用来创建machine驱动,将codec与platform(SAI)连接起来    imx-wm8960.c

platform驱动            fsl_sai.c


DAPM Widget 是音频路径上的基本构建块,代表一个输入、输出、混音器、放大器、电源等。Audio Routing 就是通过字符串匹配,将这些 Widget 的源(Source) 和接收端(Sink) 连接起来,形成完整的音频通路。

每一行的格式都是:"Sink Widget", "Source Widget",表示信号从 Source Widget 流向 Sink Widget。


dai_link 和dapm_routes的区别和联系:

dai_link 是“骨架”,负责最底层的、芯片间的数字数据传输。(codec和sai)
dapm_routes 是“神经和血管”,负责在 Codec 和板级层面进行信号的引导和分配,将数字数据“输送”到最终的物理接口。


dma初始化会调用snd_soc_add_platform


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


机器驱动、平台驱动、Codec驱动

一、platform驱动(snd_soc_dai_driver<sai>  和snd_soc_platform_driver<dma>)fsl_sai.c


    sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
    sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
    sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
    sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;

    ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
            &fsl_sai_dai, 1);
其中:
    static const struct snd_soc_component_driver fsl_component = {
        .name           = "fsl-sai",
    };

    static struct snd_soc_dai_driver fsl_sai_dai = {
        .probe = fsl_sai_dai_probe,
        .playback = {
            .stream_name = "CPU-Playback",
            .channels_min = 1,
            .channels_max = 2,
            .rate_min = 8000,
            .rate_max = 192000,
            .rates = SNDRV_PCM_RATE_KNOT,
            .formats = FSL_SAI_FORMATS,
        },
        .capture = {
            .stream_name = "CPU-Capture",
            .channels_min = 1,
            .channels_max = 2,
            .rate_min = 8000,
            .rate_max = 192000,
            .rates = SNDRV_PCM_RATE_KNOT,
            .formats = FSL_SAI_FORMATS,
        },
        .ops = &fsl_sai_pcm_dai_ops,
    };
1)在函数devm_snd_soc_register_component(&pdev->dev, &fsl_component, &fsl_sai_dai, 1);中
    snd_soc_component_initialize  //初始化struct snd_soc_component结构并赋值struct snd_soc_component_driver
    snd_soc_register_dais        //初始化struct snd_soc_dai结构并赋值snd_soc_dai_driver
            ->dai->component = component;
            ->list_add(&dai->list, &component->dai_list);
    snd_soc_component_add(cmpnt);
            ->snd_soc_component_add_unlocked(component);
                    ->list_add(&component->list, &component_list);
2)dma
    imx_pcm_dma_init(pdev, buffer_size);
        snd_soc_add_platform(dev, &pcm->platform, &dmaengine_pcm_platform);
            ret = snd_soc_component_initialize(&platform->component, &platform_drv->component_driver, dev);
            platform->driver = platform_drv;
            snd_soc_component_add_unlocked(&platform->component);
            list_add(&platform->list, &platform_list);
其中:
    static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
        .component_driver = {
            .probe_order = SND_SOC_COMP_ORDER_LATE,
        },
        .ops        = &dmaengine_pcm_ops,
        .pcm_new    = dmaengine_pcm_new,
    };
    static const struct snd_pcm_ops dmaengine_pcm_ops = {
        .open        = dmaengine_pcm_open,
        .close        = snd_dmaengine_pcm_close,
        .ioctl        = snd_pcm_lib_ioctl,
        .hw_params    = dmaengine_pcm_hw_params,
        .hw_free    = snd_pcm_lib_free_pages,
        .trigger    = snd_dmaengine_pcm_trigger,
        .pointer    = dmaengine_pcm_pointer,
    };
    
二、codec驱动 (wm8960.c)

1.wm8960_i2c_probe
        snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8960, &wm8960_dai, 1);

其中:
    static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
        .probe =    wm8960_probe,
        .set_bias_level = wm8960_set_bias_level,
        .suspend_bias_off = true,
    };

    static struct snd_soc_dai_driver wm8960_dai = {
        .name = "wm8960-hifi",
        .playback = {
            .stream_name = "Playback",
            .channels_min = 1,
            .channels_max = 2,
            .rates = WM8960_RATES,
            .formats = WM8960_FORMATS,},
        .capture = {
            .stream_name = "Capture",
            .channels_min = 1,
            .channels_max = 2,
            .rates = WM8960_RATES,
            .formats = WM8960_FORMATS,},
        .ops = &wm8960_dai_ops,
        .symmetric_rates = 1,
    };
在snd_soc_register_codec函数中:
    codec->component.codec = codec;

    ret = snd_soc_component_initialize(&codec->component, &codec_drv->component_driver, dev);
    codec->driver = codec_drv;
    ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
    list_for_each_entry(dai, &codec->component.dai_list, list)
        dai->codec = codec;
    snd_soc_component_add_unlocked(&codec->component);
    list_add(&codec->list, &codec_list);

三、machine驱动

1)在imx-wm8960.c中,有snd_soc_register_card

一、PCM的建立过程

一个pcm实例由一个playback stream和一个capture stream组成,这两个stream又分别有一个或多个substreams组成。
1)

int snd_pcm_new(struct snd_card *card, const char *id, int device,
        int playback_count, int capture_count, struct snd_pcm **rpcm)
{
    return _snd_pcm_new(card, id, device, playback_count, capture_count,
            false, rpcm);
}

2)

static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
        int playback_count, int capture_count, bool internal,
        struct snd_pcm **rpcm)
{
    struct snd_pcm *pcm;
    int err;
    static struct snd_device_ops ops = {
        .dev_free = snd_pcm_dev_free,
        .dev_register =    snd_pcm_dev_register,    //注意这个函数
        .dev_disconnect = snd_pcm_dev_disconnect,
    };

    if (snd_BUG_ON(!card))
        return -ENXIO;
    if (rpcm)
        *rpcm = NULL;
    pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
    if (!pcm)
        return -ENOMEM;
    pcm->card = card;
    pcm->device = device;
    pcm->internal = internal;
    mutex_init(&pcm->open_mutex);
    init_waitqueue_head(&pcm->open_wait);
    INIT_LIST_HEAD(&pcm->list);
    if (id)
        strlcpy(pcm->id, id, sizeof(pcm->id));
    if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
        snd_pcm_free(pcm);
        return err;
    }
    if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
        snd_pcm_free(pcm);
        return err;
    }
    if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {   //注册到&card->devices中
        snd_pcm_free(pcm);
        return err;
    }
    if (rpcm)
        *rpcm = pcm;
    return 0;
}

3)


static const struct file_operations snd_fops =
{
    .owner =    THIS_MODULE,
    .open =        snd_open,
    .llseek =    noop_llseek,
};


static int __init alsa_sound_init(void)
{
    snd_major = major;
    snd_ecards_limit = cards_limit;
    if (register_chrdev(major, "alsa", &snd_fops)) {
        pr_err("ALSA core: unable to register native major device number %d\n", major);
        return -EIO;
    }
    if (snd_info_init() < 0) {
        unregister_chrdev(major, "alsa");
        return -ENOMEM;
    }
    snd_info_minor_register();
#ifndef MODULE
    pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
    return 0;
}

static int snd_open(struct inode *inode, struct file *file)
{
    unsigned int minor = iminor(inode);
    struct snd_minor *mptr = NULL;
    const struct file_operations *new_fops;
    int err = 0;

    if (minor >= ARRAY_SIZE(snd_minors))
        return -ENODEV;
    mutex_lock(&sound_mutex);
    mptr = snd_minors[minor];       //这个mptr就是PCM音频流,这个PCM音频流是什么时候加进来的呢?(PLAYBACK和CAPTURE)
    if (mptr == NULL) {
        mptr = autoload_device(minor);
        if (!mptr) {
            mutex_unlock(&sound_mutex);
            return -ENODEV;
        }
    }
    new_fops = fops_get(mptr->f_ops);
    mutex_unlock(&sound_mutex);
    if (!new_fops)
        return -ENODEV;
    replace_fops(file, new_fops);      //这里有个替换fops的过程,会被替换成snd_pcm_f_ops或者control的(snd_ctl_f_ops),这里面的读写函数就是把数据传输给dma的buffer

    if (file->f_op->open)
        err = file->f_op->open(inode, file);
    return err;
}


4)


snd_card_register
        |
if ((err = snd_device_register_all(card)) < 0)
        |
snd_pcm_dev_register
        |
snd_register_device    //将PCM设备保存在snd_minors中


四、control设备的建立

1)
int snd_ctl_create(struct snd_card *card)
{
    static struct snd_device_ops ops = {
        .dev_free = snd_ctl_dev_free,
        .dev_register =    snd_ctl_dev_register,
        .dev_disconnect = snd_ctl_dev_disconnect,
    };
    int err;

    if (snd_BUG_ON(!card))
        return -ENXIO;
    if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
        return -ENXIO;

    snd_device_initialize(&card->ctl_dev, card);
    dev_set_name(&card->ctl_dev, "controlC%d", card->number);

    err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); //注册到&card->devices中
    if (err < 0)
        put_device(&card->ctl_dev);
    return err;
}

2)


snd_card_register
        |
if ((err = snd_device_register_all(card)) < 0)
        |
snd_ctl_dev_register
        |
snd_register_device    //将PCM设备保存在snd_minors中


在soc_new_pcm函数中,会设置substream->ops

/* ASoC PCM operations */
    if (rtd->dai_link->dynamic) {
        rtd->ops.open        = dpcm_fe_dai_open;
        rtd->ops.hw_params    = dpcm_fe_dai_hw_params;
        rtd->ops.prepare    = dpcm_fe_dai_prepare;
        rtd->ops.trigger    = dpcm_fe_dai_trigger;
        rtd->ops.hw_free    = dpcm_fe_dai_hw_free;
        rtd->ops.close        = dpcm_fe_dai_close;
        rtd->ops.pointer    = soc_pcm_pointer;
        rtd->ops.ioctl        = soc_pcm_ioctl;
    } else {
        rtd->ops.open        = soc_pcm_open;
        rtd->ops.hw_params    = soc_pcm_hw_params;
        rtd->ops.prepare    = soc_pcm_prepare;
        rtd->ops.trigger    = soc_pcm_trigger;
        rtd->ops.hw_free    = soc_pcm_hw_free;
        rtd->ops.close        = soc_pcm_close;
        rtd->ops.pointer    = soc_pcm_pointer;
        rtd->ops.ioctl        = soc_pcm_ioctl;
    }

    if (platform->driver->ops) {
        rtd->ops.ack        = platform->driver->ops->ack;
        rtd->ops.copy        = platform->driver->ops->copy;
        rtd->ops.silence    = platform->driver->ops->silence;
        rtd->ops.page        = platform->driver->ops->page;
        rtd->ops.mmap        = platform->driver->ops->mmap;
    }

    if (playback)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);

    if (capture)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);

snd_pcm_attach_substream中

有:substream->private_data = pcm->private_data;
而pcm->private_data是在soc_new_pcm中被赋值成pcm->private_data = rtd

注意区分struct snd_soc_pcm_runtime 和struct snd_pcm_runtime
ASOC框架的作用就是:
1.初始化(probe)sound对应的sai和codec
2.应用程序设置音频参数时,可以去设置对应的sai和codec的参数
3.应用程序播放音频时,可以找到对应的dma的buffer去播放数字信号。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐