Android音频驱动-ASOC之CPU DAI

来源:互联网 发布:软件开发界面 编辑:程序博客网 时间:2024/05/20 04:08

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