zoukankan      html  css  js  c++  java
  • 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
    ", 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'
    ",
            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
    ");
            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
    ",
                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;
    }
  • 相关阅读:
    IfcControlExtension (控件扩展)
    IfcKernel (内核)
    IFC4x0核心层
    IfcSharedMgmtElements (共享管理元素)
    IfcSharedFacilitiesElements (共享设施元素)
    IfcSharedComponentElements (共享组件元素)
    IfcSharedBldgServiceElements (共享建筑服务要素)
    IfcSharedBldgElements (共享建筑元素)
    IFC4x0共享层
    IfcStructuralElementsDomain (结构元素领域)
  • 原文地址:https://www.cnblogs.com/fellow1988/p/12632185.html
Copyright © 2011-2022 走看看