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;
}

 

posted @ 2020-04-04 15:56  fellow_jing  阅读(1061)  评论(0编辑  收藏  举报