Android音頻驅動-ASOC之CPU DAI

dai驅動通常對應cpu的一個或幾個I2S/PCM接口,與snd_soc_platform一樣,dai驅動也是實現爲一個platform driver,
實現一個dai驅動大致可以分爲以下幾個步驟:

定義一個snd_soc_dai_driver結構的實例;
通過API snd_soc_register_dais,註冊snd_soc_dai實例;
實現snd_soc_dai_driver結構中的probe、suspend等回調;
實現snd_soc_dai_driver結構中的snd_soc_dai_ops字段中的回調函數;
struct snd_soc_dai_driver {
    /* DAI description */
    const char *name;
    unsigned int id;
    int ac97_control;
    unsigned int base;

    /* DAI driver callbacks */
    int (*probe)(struct snd_soc_dai *dai);
    int (*remove)(struct snd_soc_dai *dai);
    int (*suspend)(struct snd_soc_dai *dai);
    int (*resume)(struct snd_soc_dai *dai);
    /* compress dai */
    bool compress_dai;

    /* ops */
    const struct snd_soc_dai_ops *ops;

    /* DAI capabilities */
    struct snd_soc_pcm_stream capture;
    struct snd_soc_pcm_stream playback;
    unsigned int symmetric_rates:1;
    unsigned int symmetric_channels:1;
    unsigned int symmetric_samplebits:1;

    /* probe ordering - for components with runtime dependencies */
    int probe_order;
    int remove_order;
};
struct snd_soc_dai_ops {
    /*
     * DAI clocking configuration, all optional.
     * Called by soc_card drivers, normally in their hw_params.
     */
    int (*set_sysclk)(struct snd_soc_dai *dai,
        int clk_id, unsigned int freq, int dir);
    int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
        unsigned int freq_in, unsigned int freq_out);
    int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
    int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);

    /*
     * DAI format configuration
     * Called by soc_card drivers, normally in their hw_params.
     */
    int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
    int (*xlate_tdm_slot_mask)(unsigned int slots,
        unsigned int *tx_mask, unsigned int *rx_mask);
    int (*set_tdm_slot)(struct snd_soc_dai *dai,
        unsigned int tx_mask, unsigned int rx_mask,
        int slots, int slot_width);
    int (*set_channel_map)(struct snd_soc_dai *dai,
        unsigned int tx_num, unsigned int *tx_slot,
        unsigned int rx_num, unsigned int *rx_slot);
    int (*set_tristate)(struct snd_soc_dai *dai, int tristate);

    /*
     * DAI digital mute - optional.
     * Called by soc-core to minimise any pops.
     */
    int (*digital_mute)(struct snd_soc_dai *dai, int mute);
    int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);

    /*
     * ALSA PCM audio operations - all optional.
     * Called by soc-core during audio PCM operations.
     */
    int (*startup)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
    void (*shutdown)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
    int (*hw_params)(struct snd_pcm_substream *,
        struct snd_pcm_hw_params *, struct snd_soc_dai *);
    int (*hw_free)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
    int (*prepare)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
    /*
     * NOTE: Commands passed to the trigger function are not necessarily
     * compatible with the current state of the dai. For example this
     * sequence of commands is possible: START STOP STOP.
     * So do not unconditionally use refcounting functions in the trigger
     * function, e.g. clk_enable/disable.
     */
    int (*trigger)(struct snd_pcm_substream *, int,
        struct snd_soc_dai *);
    int (*bespoke_trigger)(struct snd_pcm_substream *, int,
        struct snd_soc_dai *);
    /*
     * For hardware based FIFO caused delay reporting.
     * Optional.
     */
    snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
};
static int __init mtk_dai_stub_init(void)
{
    int ret;
    soc_mtk_dai_dev = platform_device_alloc(MT_SOC_DAI_NAME , -1);
    ret = platform_device_add(soc_mtk_dai_dev);
    return platform_driver_register(&mtk_dai_stub_driver);
}
/*static struct platform_driver mtk_dai_stub_driver = {//Linux platform driver
    .probe  = mtk_dai_stub_dev_probe,
    .remove = mtk_dai_stub_dev_remove,
    .driver = {
        .name = MT_SOC_DAI_NAME,
        .owner = THIS_MODULE,
    },
};
static struct snd_soc_dai_ops mtk_dai_stub_ops = {
    .startup    = multimedia_startup,
};
static struct snd_soc_dai_driver mtk_dai_stub_dai[] = {//cpu dai driver
    {
        .playback = {
            .stream_name = MT_SOC_I2SDL1_STREAM_NAME,
            .rates = SNDRV_PCM_RATE_8000_192000,
            .formats = SND_SOC_ADV_MT_FMTS,
            .channels_min = 1,
            .channels_max = 2,
            .rate_min = 8000,
            .rate_max = 192000,
        },
        .name = MT_SOC_I2S0DL1_NAME,
        .ops = &mtk_dai_stub_ops,
    },
        ......
}
static const struct snd_soc_component_driver mt_dai_component = {//CPU DAI的所屬的component driver對象
    .name       = MT_SOC_DAI_NAME,
};*/
static int mtk_dai_stub_dev_probe(struct platform_device *pdev)
{
    int rc = 0;
    pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);
    if (pdev->dev.of_node)
        dev_set_name(&pdev->dev, "%s", MT_SOC_DAI_NAME);
    rc = snd_soc_register_component(&pdev->dev, &mt_dai_component,
                    mtk_dai_stub_dai, ARRAY_SIZE(mtk_dai_stub_dai));
    return rc;
}
int snd_soc_register_component(struct device *dev,
                   const struct snd_soc_component_driver *cmpnt_drv,
                   struct snd_soc_dai_driver *dai_drv,
                   int num_dai)
{
    struct snd_soc_component *cmpnt;
    int ret;

    cmpnt = kzalloc(sizeof(*cmpnt), GFP_KERNEL);//創建CPU DAI所屬的component對象

    ret = snd_soc_component_initialize(cmpnt, cmpnt_drv, dev);

    cmpnt->ignore_pmdown_time = true;
    cmpnt->registered_as_component = true;

    ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true);//將CPU DAI保存到component->dai_list鏈表中

    snd_soc_component_add(cmpnt);//將component保存到全局對象component_list中

    return 0;
}

創建CPU DAI所屬的component對象

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);
    component->dev = dev;
    component->driver = driver;//保存mt_dai_component
    component->probe = component->driver->probe;//component driver對象沒有實現probe函數
    component->remove = component->driver->remove;

    if (!component->dapm_ptr)
        component->dapm_ptr = &component->dapm;

    dapm = component->dapm_ptr;
    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;
}

將CPU DAI保存到component->dai_list鏈表中

static int snd_soc_register_dais(struct snd_soc_component *component,
    struct snd_soc_dai_driver *dai_drv, size_t count,
    bool legacy_dai_naming)
{
    struct device *dev = component->dev;
    struct snd_soc_dai *dai;
    unsigned int i;
    int ret;
    component->dai_drv = dai_drv;
    component->num_dai = count;

    for (i = 0; i < count; i++) {
        dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);

        if (count == 1 && legacy_dai_naming) {
            dai->name = fmt_single_name(dev, &dai->id);
        } else {
            dai->name = fmt_multiple_name(dev, &dai_drv[i]);
            if (dai_drv[i].id)
                dai->id = dai_drv[i].id;
            else
                dai->id = i;
        }
        dai->component = component;
        dai->dev = dev;
        dai->driver = &dai_drv[i];
        if (!dai->driver->ops)
            dai->driver->ops = &null_dai_ops;

        list_add(&dai->list, &component->dai_list);
    }

    return 0;
}

將component保存到全局對象component_list中

static void snd_soc_component_add(struct snd_soc_component *component)
{
    mutex_lock(&client_mutex);
    snd_soc_component_add_unlocked(component);
    mutex_unlock(&client_mutex);
}
static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
{
    if (!component->write && !component->read)
        snd_soc_component_init_regmap(component);
    list_add(&component->list, &component_list);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章