ALSA driver---register platform

platform to register by calling snd_soc_register_platform. snd_soc_register_platform () This function is used to register a snd_soc_platform, only after the registration, it can be driven using the 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. First, the structure of the memory allocation of snd_soc_platform, snd_soc_platform described platform currently registered, snd_soc_platform_driver platform is the corresponding 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 to initialize the platform, filling structures in the body member, and register.

/**
 * 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 to initialize the component members of the platform by snd_soc_component_initialize.

snd_soc_component structure as follows:

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 is a very important structure, platform / codec / dai contains component. component contains conponent driver, an important component of the information and dai list dai driver, dapm contex, dapm widget , damp route and so on.

These information is important assignment in snd_soc_component_initializer. When subsequent register card, is to find the platform by matching component-> name.

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 members assigned to the driver's platform.

/* 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 consists mainly of a DMA operation functions. The following is an example of a platform driver, from 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 calls snd_soc_component_add_unlocked the platform of the component is added to the global list 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);
}

The platform 2.4 is added to the global list of platform_list.

So far snd_soc_platform has been allocated, initialization is complete, and added to platform_list in.

In a subsequent snd_soc_register_card-> snd_soc_instantiate_card soc_bind_dai_link will call the function, this function loops through PLATFORM_LIST, Comparative platform-> component.name and dai_link-> platform_name, to find the corresponding 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;
    }

In the following Machine, for example, dai_link of platform_name is 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,
};

Find pxa-pcm-audio names platform, the corresponding platform driver as follows,

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

 

Probe will be in soc_probe_link_components platform in the component:

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

 

Guess you like

Origin www.cnblogs.com/fellow1988/p/12632185.html