ALSA driver---register platform

參考:

https://elixir.bootlin.com/linux/v4.9.218/source/sound/soc/soc-core.c#L3159

https://blog.csdn.net/DroidPhone/article/details/7316061

 

platform通過調用snd_soc_register_platform來註冊。snd_soc_register_platform() 該函數用於註冊一個snd_soc_platform,只有註冊以後,它纔可以被Machine驅動使用。

複製代碼

/**
 * snd_soc_register_platform - Register a platform with the ASoC core
 *
 * @dev: The device for the platform
 * @platform_drv: The driver for the platform
 */
int snd_soc_register_platform(struct device *dev,
        const struct snd_soc_platform_driver *platform_drv)
{
    struct snd_soc_platform *platform;
    int ret;

    dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev));

    platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
    if (platform == NULL)
        return -ENOMEM;

    ret = snd_soc_add_platform(dev, platform, platform_drv);
    if (ret)
        kfree(platform);

    return ret;
}

複製代碼

1.首先分配snd_soc_platform的結構體的內存,snd_soc_platform描述當前註冊的platform,snd_soc_platform_driver是platform對應的driver.

複製代碼

struct snd_soc_platform {
    struct device *dev;
    const struct snd_soc_platform_driver *driver;

    struct list_head list;

    struct snd_soc_component component;
};

複製代碼

2.snd_soc_add_platform對platform進行初始化,填充結構體內成員,並進行註冊。

複製代碼

/**
 * snd_soc_add_platform - Add a platform to the ASoC core
 * @dev: The parent device for the platform
 * @platform: The platform to add
 * @platform_drv: The driver for the platform
 */
int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
        const struct snd_soc_platform_driver *platform_drv)
{
    int ret;

    ret = snd_soc_component_initialize(&platform->component,
            &platform_drv->component_driver, dev);
    if (ret)
        return ret;

    platform->dev = dev;
    platform->driver = platform_drv;

    if (platform_drv->probe)
        platform->component.probe = snd_soc_platform_drv_probe;
    if (platform_drv->remove)
        platform->component.remove = snd_soc_platform_drv_remove;

#ifdef CONFIG_DEBUG_FS
    platform->component.debugfs_prefix = "platform";
#endif

    mutex_lock(&client_mutex);
    snd_soc_component_add_unlocked(&platform->component);
    list_add(&platform->list, &platform_list);
    mutex_unlock(&client_mutex);

    dev_dbg(dev, "ASoC: Registered platform '%s'\n",
        platform->component.name);

    return 0;
}

複製代碼

2.1通過snd_soc_component_initialize來初始化platform的component 成員。

snd_soc_component結構體如下:

複製代碼

struct snd_soc_component {
    const char *name;
    int id;
    const char *name_prefix;
    struct device *dev;
    struct snd_soc_card *card;

    unsigned int active;

    unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
    unsigned int registered_as_component:1;

    struct list_head list;
    struct list_head list_aux; /* for auxiliary component of the card */

    struct snd_soc_dai_driver *dai_drv;
    int num_dai;

    const struct snd_soc_component_driver *driver;

    struct list_head dai_list;

    int (*read)(struct snd_soc_component *, unsigned int, unsigned int *);
    int (*write)(struct snd_soc_component *, unsigned int, unsigned int);

    struct regmap *regmap;
    int val_bytes;

    struct mutex io_mutex;

    /* attached dynamic objects */
    struct list_head dobj_list;

#ifdef CONFIG_DEBUG_FS
    struct dentry *debugfs_root;
#endif

    /*
    * DO NOT use any of the fields below in drivers, they are temporary and
    * are going to be removed again soon. If you use them in driver code the
    * driver will be marked as BROKEN when these fields are removed.
    */

    /* Don't use these, use snd_soc_component_get_dapm() */
    struct snd_soc_dapm_context dapm;

    const struct snd_kcontrol_new *controls;
    unsigned int num_controls;
    const struct snd_soc_dapm_widget *dapm_widgets;
    unsigned int num_dapm_widgets;
    const struct snd_soc_dapm_route *dapm_routes;
    unsigned int num_dapm_routes;
    struct snd_soc_codec *codec;

    int (*probe)(struct snd_soc_component *);
    void (*remove)(struct snd_soc_component *);

    /* machine specific init */
    int (*init)(struct snd_soc_component *component);

#ifdef CONFIG_DEBUG_FS
    void (*init_debugfs)(struct snd_soc_component *component);
    const char *debugfs_prefix;
#endif
};

複製代碼

snd_soc_component是非常重要的結構體,platform/codec/cpu dai都包含component。component中包含conponent driver, 該component的dai list和dai driver, dapm contex, dapm widget, damp route等重要信息。

在snd_soc_component_initializer就是對這些重要信息進行賦值。後續在register card時,就是通過匹配component->name來找到相應的platform.

複製代碼

static int snd_soc_component_initialize(struct snd_soc_component *component,
    const struct snd_soc_component_driver *driver, struct device *dev)
{
    struct snd_soc_dapm_context *dapm;

    component->name = fmt_single_name(dev, &component->id);
    if (!component->name) {
        dev_err(dev, "ASoC: Failed to allocate name\n");
        return -ENOMEM;
    }

    component->dev = dev;
    component->driver = driver;
    component->probe = component->driver->probe;
    component->remove = component->driver->remove;

    dapm = &component->dapm;
    dapm->dev = dev;
    dapm->component = component;
    dapm->bias_level = SND_SOC_BIAS_OFF;
    dapm->idle_bias_off = true;
    if (driver->seq_notifier)
        dapm->seq_notifier = snd_soc_component_seq_notifier;
    if (driver->stream_event)
        dapm->stream_event = snd_soc_component_stream_event;

    component->controls = driver->controls;
    component->num_controls = driver->num_controls;
    component->dapm_widgets = driver->dapm_widgets;
    component->num_dapm_widgets = driver->num_dapm_widgets;
    component->dapm_routes = driver->dapm_routes;
    component->num_dapm_routes = driver->num_dapm_routes;

    INIT_LIST_HEAD(&component->dai_list);
    mutex_init(&component->io_mutex);

    return 0;
}

複製代碼

2.2 將snd_soc_platform_driver 賦值給platform的driver成員。

複製代碼

/* SoC platform interface */
struct snd_soc_platform_driver {

    int (*probe)(struct snd_soc_platform *);
    int (*remove)(struct snd_soc_platform *);
    struct snd_soc_component_driver component_driver;

    /* pcm creation and destruction */
    int (*pcm_new)(struct snd_soc_pcm_runtime *);
    void (*pcm_free)(struct snd_pcm *);

    /*
     * For platform caused delay reporting.
     * Optional.
     */
    snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
        struct snd_soc_dai *);

    /* platform stream pcm ops */
    const struct snd_pcm_ops *ops;

    /* platform stream compress ops */
    const struct snd_compr_ops *compr_ops;

    int (*bespoke_trigger)(struct snd_pcm_substream *, int);
};

複製代碼

snd_soc_platform_driver主要包含了對DMA的操作函數。以下是一個platform driver的例子,來源於sound/soc/pxa/pxa2xx-pcm.c

複製代碼

static struct snd_pcm_ops pxa2xx_pcm_ops = {
    .open        = __pxa2xx_pcm_open,
    .close        = __pxa2xx_pcm_close,
    .ioctl        = snd_pcm_lib_ioctl,
    .hw_params    = pxa2xx_pcm_hw_params,
    .hw_free    = pxa2xx_pcm_hw_free,
    .prepare    = __pxa2xx_pcm_prepare,
    .trigger    = pxa2xx_pcm_trigger,
    .pointer    = pxa2xx_pcm_pointer,
    .mmap        = pxa2xx_pcm_mmap,
};
static struct snd_soc_platform_driver pxa2xx_soc_platform = {
    .ops     = &pxa2xx_pcm_ops,
    .pcm_new    = pxa2xx_soc_pcm_new,
    .pcm_free    = pxa2xx_pcm_free_dma_buffers,
};

複製代碼

2.3調用snd_soc_component_add_unlocked將platform的component加到全局列表component_list中。

複製代碼

static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
{
    if (!component->write && !component->read) {
        if (!component->regmap)
            component->regmap = dev_get_regmap(component->dev, NULL);
        if (component->regmap)
            snd_soc_component_setup_regmap(component);
    }

    list_add(&component->list, &component_list);
    INIT_LIST_HEAD(&component->dobj_list);
}

複製代碼

2.4將platform加到全局列表platform_list中。

至此snd_soc_platform已經分配,初始化完成,並添加到platform_list中。

在後續snd_soc_register_card->snd_soc_instantiate_card中會調用soc_bind_dai_link函數,在此函數中會遍歷整個platform_list,比較platform->component.name和dai_link->platform_name,從而找到對應的platform.

複製代碼

/* if there's no platform we match on the empty platform */
    platform_name = dai_link->platform_name;
    if (!platform_name && !dai_link->platform_of_node)
        platform_name = "snd-soc-dummy";

    /* find one from the set of registered platforms */
    list_for_each_entry(platform, &platform_list, list) {
        if (dai_link->platform_of_node) {
            if (platform->dev->of_node !=
                dai_link->platform_of_node)
                continue;
        } else {
            if (strcmp(platform->component.name, platform_name))
                continue;
        }

        rtd->platform = platform;
    }
    if (!rtd->platform) {
        dev_err(card->dev, "ASoC: platform %s not registered\n",
            dai_link->platform_name);
        goto _err_defer;
    }

複製代碼

以下面的Machine爲例, dai_link的platform_name爲pxa-pcm-audio

複製代碼

static struct snd_soc_dai_link corgi_dai = {
    .name = "WM8731",
    .stream_name = "WM8731",
    .cpu_dai_name = "pxa2xx-i2s",
    .codec_dai_name = "wm8731-hifi",
    .platform_name = "pxa-pcm-audio",
    .codec_name = "wm8731.0-001b",
    .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
           SND_SOC_DAIFMT_CBS_CFS,
    .ops = &corgi_ops,
};

複製代碼

會找到pxa-pcm-audio名字的platform,對應的platform driver如下,

複製代碼

static struct snd_soc_platform_driver pxa2xx_soc_platform = {
    .ops     = &pxa2xx_pcm_ops,
    .pcm_new    = pxa2xx_soc_pcm_new,
    .pcm_free    = pxa2xx_pcm_free_dma_buffers,
};

static int pxa2xx_soc_platform_probe(struct platform_device *pdev)
{
    return devm_snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform);
}

#ifdef CONFIG_OF
static const struct of_device_id snd_soc_pxa_audio_match[] = {
    { .compatible   = "mrvl,pxa-pcm-audio" },
    { }
};
MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match);
#endif

static struct platform_driver pxa_pcm_driver = {
    .driver = {
        .name = "pxa-pcm-audio",
        .of_match_table = of_match_ptr(snd_soc_pxa_audio_match),
    },

    .probe = pxa2xx_soc_platform_probe,
};

複製代碼

 

在soc_probe_link_components中會對platform的component進行probe:

複製代碼

static int soc_probe_link_components(struct snd_soc_card *card,
            struct snd_soc_pcm_runtime *rtd,
                     int order)
{
    struct snd_soc_platform *platform = rtd->platform;
    struct snd_soc_component *component;
    int i, ret;

    /* probe the CPU-side component, if it is a CODEC */
    component = rtd->cpu_dai->component;
    if (component->driver->probe_order == order) {
        ret = soc_probe_component(card, component);
        if (ret < 0)
            return ret;
    }

    /* probe the CODEC-side components */
    for (i = 0; i < rtd->num_codecs; i++) {
        component = rtd->codec_dais[i]->component;
        if (component->driver->probe_order == order) {
            ret = soc_probe_component(card, component);
            if (ret < 0)
                return ret;
        }
    }

    /* probe the platform */
    if (platform->component.driver->probe_order == order) {
        ret = soc_probe_component(card, &platform->component);
        if (ret < 0)
            return ret;
    }

    return 0;
}

複製代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章