msm audio machine 代码跟踪


sound/soc/msm/msm8952.c

// 注册平台设备
static int __init msm8952_machine_init(void)
{
    return platform_driver_register(&msm8952_asoc_machine_driver);
}
late_initcall(msm8952_machine_init);

// 驱动和设备的匹配表
static const struct of_device_id msm8952_asoc_machine_of_match[]  = {
    { .compatible = "qcom,msm8952-audio-codec", },
    {},
};

static struct platform_driver msm8952_asoc_machine_driver = {
    .driver = {
        .name = DRV_NAME,
        .owner = THIS_MODULE,
        .pm = &snd_soc_pm_ops,
        .of_match_table = msm8952_asoc_machine_of_match,
    },
    .probe = msm8952_asoc_machine_probe,
    .remove = msm8952_asoc_machine_remove,
};

static int msm8952_asoc_machine_probe(struct platform_device *pdev)
{
    struct snd_soc_card *card;
    struct msm8916_asoc_mach_data *pdata = NULL;
    const char *hs_micbias_type = "qcom,msm-hs-micbias-type";
    const char *ext_pa = "qcom,msm-ext-pa";
    const char *mclk = "qcom,msm-mclk-freq";
    const char *wsa = "asoc-wsa-codec-names";
    const char *wsa_prefix = "asoc-wsa-codec-prefixes";
    const char *type = NULL;
    const char *ext_pa_str = NULL;
    const char *wsa_str = NULL;
    const char *wsa_prefix_str = NULL;
    int num_strings;
    int ret, id, i, val;
    struct resource *muxsel;
    char *temp_str = NULL;

    pdata = devm_kzalloc(&pdev->dev,
            sizeof(struct msm8916_asoc_mach_data), GFP_KERNEL);
    if (!pdata)
        return -ENOMEM;

    muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
            "csr_gp_io_mux_mic_ctl");
    if (!muxsel) {
        dev_err(&pdev->dev, "MUX addr invalid for MI2S\n");
        ret = -ENODEV;
        goto err1;
    }
    pdata->vaddr_gpio_mux_mic_ctl =
        ioremap(muxsel->start, resource_size(muxsel));
    if (pdata->vaddr_gpio_mux_mic_ctl == NULL) {
        pr_err("%s ioremap failure for muxsel virt addr\n",
                __func__);
        ret = -ENOMEM;
        goto err1;
    }

    muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
            "csr_gp_io_mux_spkr_ctl");
    if (!muxsel) {
        dev_err(&pdev->dev, "MUX addr invalid for MI2S\n");
        ret = -ENODEV;
        goto err;
    }
    pdata->vaddr_gpio_mux_spkr_ctl =
        ioremap(muxsel->start, resource_size(muxsel));
    if (pdata->vaddr_gpio_mux_spkr_ctl == NULL) {
        pr_err("%s ioremap failure for muxsel virt addr\n",
                __func__);
        ret = -ENOMEM;
        goto err;
    }

    muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
            "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel");
    if (!muxsel) {
        dev_err(&pdev->dev, "MUX addr invalid for MI2S\n");
        ret = -ENODEV;
        goto err;
    }
    pdata->vaddr_gpio_mux_pcm_ctl =
        ioremap(muxsel->start, resource_size(muxsel));
    if (pdata->vaddr_gpio_mux_pcm_ctl == NULL) {
        pr_err("%s ioremap failure for muxsel virt addr\n",
                __func__);
        ret = -ENOMEM;
        goto err;
    }

    muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
            "csr_gp_io_mux_quin_ctl");
    if (!muxsel) {
        dev_dbg(&pdev->dev, "MUX addr invalid for MI2S\n");
        goto parse_mclk_freq;
    }
    pdata->vaddr_gpio_mux_quin_ctl =
        ioremap(muxsel->start, resource_size(muxsel));
    if (pdata->vaddr_gpio_mux_quin_ctl == NULL) {
        pr_err("%s ioremap failure for muxsel virt addr\n",
                __func__);
        ret = -ENOMEM;
        goto err;
    }
parse_mclk_freq:
    // 获取时钟
    ret = of_property_read_u32(pdev->dev.of_node, mclk, &id);
    if (ret) {
        dev_err(&pdev->dev,
                "%s: missing %s in dt node\n", __func__, mclk);
        id = DEFAULT_MCLK_RATE;
    }
    pdata->mclk_freq = id;

    /*reading the gpio configurations from dtsi file*/
    ret = msm_gpioset_initialize(CLIENT_WCD_INT, &pdev->dev);
    if (ret < 0) {
        dev_err(&pdev->dev,
            "%s: error reading dtsi files%d\n", __func__, ret);
        goto err;
    }

    num_strings = of_property_count_strings(pdev->dev.of_node,
            wsa);
    if (num_strings > 0) {
        if (wsa881x_get_probing_count() < 2) {
            ret = -EPROBE_DEFER;
            goto err;
        } else if (wsa881x_get_presence_count() == num_strings) {
            bear_card.aux_dev = msm8952_aux_dev;
            bear_card.num_aux_devs = num_strings;
            bear_card.codec_conf = msm8952_codec_conf;
            bear_card.num_configs = num_strings;

            for (i = 0; i < num_strings; i++) {
                ret = of_property_read_string_index(
                        pdev->dev.of_node, wsa,
                        i, &wsa_str);
                if (ret) {
                    dev_err(&pdev->dev,
                        "%s:of read string %s i %d error %d\n",
                        __func__, wsa, i, ret);
                    goto err;
                }
                temp_str = kstrdup(wsa_str, GFP_KERNEL);
                if (!temp_str) {
                    ret = -ENOMEM;
                    goto err;
                }
                msm8952_aux_dev[i].codec_name = temp_str;
                temp_str = NULL;

                temp_str = kstrdup(wsa_str, GFP_KERNEL);
                if (!temp_str) {
                    ret = -ENOMEM;
                    goto err;
                }
                msm8952_codec_conf[i].dev_name = temp_str;
                temp_str = NULL;

                ret = of_property_read_string_index(
                        pdev->dev.of_node, wsa_prefix,
                        i, &wsa_prefix_str);
                if (ret) {
                    dev_err(&pdev->dev,
                        "%s:of read string %s i %d error %d\n",
                        __func__, wsa_prefix, i, ret);
                    goto err;
                }
                temp_str = kstrdup(wsa_prefix_str, GFP_KERNEL);
                if (!temp_str) {
                    ret = -ENOMEM;
                    goto err;
                }
                msm8952_codec_conf[i].name_prefix = temp_str;
                temp_str = NULL;
            }

            ret = msm8952_init_wsa_switch_supply(pdev, pdata);
            if (ret < 0) {
                pr_err("%s: failed to init wsa_switch vdd supply %d\n",
                        __func__, ret);
                goto err;
            }
            wsa881x_set_mclk_callback(msm8952_enable_wsa_mclk);
            /* update the internal speaker boost usage */
            msm8x16_update_int_spk_boost(false);
        }
    }
    // 获取dai_links
    card = msm8952_populate_sndcard_dailinks(&pdev->dev);
    dev_info(&pdev->dev, "default codec configured\n");
    num_strings = of_property_count_strings(pdev->dev.of_node,
            ext_pa);
    if (num_strings < 0) {
        dev_err(&pdev->dev,
                "%s: missing %s in dt node or length is incorrect\n",
                __func__, ext_pa);
        goto err;
    }
    for (i = 0; i < num_strings; i++) {
        ret = of_property_read_string_index(pdev->dev.of_node,
                ext_pa, i, &ext_pa_str);
        if (ret) {
            dev_err(&pdev->dev, "%s:of read string %s i %d error %d\n",
                    __func__, ext_pa, i, ret);
            goto err;
        }
        if (!strcmp(ext_pa_str, "primary"))
            pdata->ext_pa = (pdata->ext_pa | PRI_MI2S_ID);
        else if (!strcmp(ext_pa_str, "secondary"))
            pdata->ext_pa = (pdata->ext_pa | SEC_MI2S_ID);
        else if (!strcmp(ext_pa_str, "tertiary"))
            pdata->ext_pa = (pdata->ext_pa | TER_MI2S_ID);
        else if (!strcmp(ext_pa_str, "quaternary"))
            pdata->ext_pa = (pdata->ext_pa | QUAT_MI2S_ID);
        else if (!strcmp(ext_pa_str, "quinary"))
            pdata->ext_pa = (pdata->ext_pa | QUIN_MI2S_ID);
    }
    pr_debug("%s: ext_pa = %d\n", __func__, pdata->ext_pa);

    ret = is_us_eu_switch_gpio_support(pdev, pdata);
    if (ret < 0) {
        pr_err("%s: failed to is_us_eu_switch_gpio_support %d\n",
                __func__, ret);
        goto err;
    }

    ret = is_ext_spk_gpio_support(pdev, pdata);
    if (ret < 0)
        pr_err("%s:  doesn't support external speaker pa\n",
                __func__);

        get_dev_by_boardid(model_name);
        if(0 == strcmp(model_name,"eda71")){
            enable_aw8738 = of_property_read_bool(pdev->dev.of_node , "action,enable-aw8738");
            if(enable_aw8738)
            {
                gpio_request(pdata->spk_ext_pa_gpio, "AW8738_EN");
                gpio_direction_output(pdata->spk_ext_pa_gpio,0);
            }
        }
        //printk("enable_aw8738:%d model_name:%s.\n",enable_aw8738,model_name);
    // 查看micbias类型
    ret = of_property_read_string(pdev->dev.of_node,
        hs_micbias_type, &type);
    if (ret) {
        dev_err(&pdev->dev, "%s: missing %s in dt node\n",
            __func__, hs_micbias_type);
        goto err;
    }
    // 使用内部还是外部的micbias
    if (!strcmp(type, "external")) {
        dev_dbg(&pdev->dev, "Headset is using external micbias\n");
        mbhc_cfg.hs_ext_micbias = true;
    } else {
        dev_dbg(&pdev->dev, "Headset is using internal micbias\n");
        mbhc_cfg.hs_ext_micbias = false;
    }

    ret = of_property_read_u32(pdev->dev.of_node,
                  "qcom,msm-afe-clk-ver", &val);
    if (ret)
        pdata->afe_clk_ver = AFE_CLK_VERSION_V2;
    else
        pdata->afe_clk_ver = val;
    /* initialize the mclk */
    pdata->digital_cdc_clk.i2s_cfg_minor_version =
                    AFE_API_VERSION_I2S_CONFIG;
    pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
    pdata->digital_cdc_clk.clk_root = 5;
    pdata->digital_cdc_clk.reserved = 0;
    /* initialize the digital codec core clk */
    pdata->digital_cdc_core_clk.clk_set_minor_version =
            AFE_API_VERSION_I2S_CONFIG;
    pdata->digital_cdc_core_clk.clk_id =
            Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE;
    pdata->digital_cdc_core_clk.clk_freq_in_hz =
            pdata->mclk_freq;
    pdata->digital_cdc_core_clk.clk_attri =
            Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
    pdata->digital_cdc_core_clk.clk_root =
            Q6AFE_LPASS_CLK_ROOT_DEFAULT;
    pdata->digital_cdc_core_clk.enable = 1;

    /* Initialize loopback mode to false */
    pdata->lb_mode = false;

    msm8952_dt_parse_cap_info(pdev, pdata);

    card->dev = &pdev->dev;
    platform_set_drvdata(pdev, card);
    snd_soc_card_set_drvdata(card, pdata);
    // 解析设备数中声卡的名称, qcom,model = "msm8953-snd-card-mtp";
    ret = snd_soc_of_parse_card_name(card, "qcom,model");
    if (ret)
        goto err;
    /* initialize timer */
    INIT_DELAYED_WORK(&pdata->disable_mclk_work, msm8952_disable_mclk);
    mutex_init(&pdata->cdc_mclk_mutex);
    atomic_set(&pdata->mclk_rsc_ref, 0);
    if (card->aux_dev) {
        mutex_init(&pdata->wsa_mclk_mutex);
        atomic_set(&pdata->wsa_mclk_rsc_ref, 0);
    }
    atomic_set(&pdata->mclk_enabled, false);
    atomic_set(&quat_mi2s_clk_ref, 0);
    atomic_set(&quin_mi2s_clk_ref, 0);
    atomic_set(&auxpcm_mi2s_clk_ref, 0);
    // 声卡的routing
    ret = snd_soc_of_parse_audio_routing(card,
            "qcom,audio-routing");
    if (ret)
        goto err;

    ret = msm8952_populate_dai_link_component_of_node(card);
    if (ret) {
        ret = -EPROBE_DEFER;
        goto err;
    }
    // 注册声卡
    ret = snd_soc_register_card(card);
    if (ret) {
        dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
            ret);
        goto err;
    }
    return 0;
err:
    if (pdata->vaddr_gpio_mux_spkr_ctl)
        iounmap(pdata->vaddr_gpio_mux_spkr_ctl);
    if (pdata->vaddr_gpio_mux_mic_ctl)
        iounmap(pdata->vaddr_gpio_mux_mic_ctl);
    if (pdata->vaddr_gpio_mux_pcm_ctl)
        iounmap(pdata->vaddr_gpio_mux_pcm_ctl);
    if (pdata->vaddr_gpio_mux_quin_ctl)
        iounmap(pdata->vaddr_gpio_mux_quin_ctl);
    if (bear_card.num_aux_devs > 0) {
        for (i = 0; i < bear_card.num_aux_devs; i++) {
            kfree(msm8952_aux_dev[i].codec_name);
            kfree(msm8952_codec_conf[i].dev_name);
            kfree(msm8952_codec_conf[i].name_prefix);
        }
    }
err1:
    devm_kfree(&pdev->dev, pdata);
    return ret;
}

static struct snd_soc_card *msm8952_populate_sndcard_dailinks(
                        struct device *dev)
{
    struct snd_soc_card *card = &bear_card;
    struct snd_soc_dai_link *dailink;
    int len1;

    card->name = dev_name(dev);
    len1 = ARRAY_SIZE(msm8952_dai);
    // dai_links复制给snd_soc_card, 注册的时候会用到
    memcpy(msm8952_dai_links, msm8952_dai, sizeof(msm8952_dai));
    dailink = msm8952_dai_links;
    if (of_property_read_bool(dev->of_node,
                "qcom,hdmi-dba-codec-rx")) {
        dev_dbg(dev, "%s(): hdmi audio support present\n",
                __func__);
        memcpy(dailink + len1, msm8952_hdmi_dba_dai_link,
                sizeof(msm8952_hdmi_dba_dai_link));
        len1 += ARRAY_SIZE(msm8952_hdmi_dba_dai_link);
    } else {
        dev_dbg(dev, "%s(): No hdmi dba present, add quin dai\n",
                __func__);
        memcpy(dailink + len1, msm8952_quin_dai_link,
                sizeof(msm8952_quin_dai_link));
        len1 += ARRAY_SIZE(msm8952_quin_dai_link);
    }
    if (of_property_read_bool(dev->of_node,
                "qcom,split-a2dp")) {
        dev_dbg(dev, "%s(): split a2dp support present\n",
                __func__);
        memcpy(dailink + len1, msm8952_split_a2dp_dai_link,
                sizeof(msm8952_split_a2dp_dai_link));
        len1 += ARRAY_SIZE(msm8952_split_a2dp_dai_link);
    }
    card->dai_link = dailink;
    card->num_links = len1;
    return card;
}



// machine驱动会用这些参数去匹配platform,codec,adi.
// 都是在platform,codec中定义
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link msm8952_dai[] = {
    /* FrontEnd DAI Links */
    {/* hw:x,0 */
        .name = "MSM8952 Media1",       // Media1 播放链路
        .stream_name = "MultiMedia1",   // 匹配pcm id
        .cpu_dai_name   = "MultiMedia1",    // 匹配cpu dai driver
        .platform_name  = "msm-pcm-dsp.0",  // 匹配platform driver
        .dynamic = 1,
        .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
        .dpcm_playback = 1,
        .dpcm_capture = 1,
        .trigger = {SND_SOC_DPCM_TRIGGER_POST,
            SND_SOC_DPCM_TRIGGER_POST},
        .codec_dai_name = "snd-soc-dummy-dai",  // 匹配codec dai driver
        .codec_name = "snd-soc-dummy",  // 匹配codec driver
        .ignore_suspend = 1,
        /* this dainlink has playback support */
        .ignore_pmdown_time = 1,
        .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
    },
    {/* hw:x,1 */
        .name = "MSM8952 Media2",       // Media2播放链路
        .stream_name = "MultiMedia2",
        .cpu_dai_name   = "MultiMedia2",
        .platform_name  = "msm-pcm-dsp.0",
        .dynamic = 1,
        .dpcm_playback = 1,
        .dpcm_capture = 1,
        .codec_dai_name = "snd-soc-dummy-dai",
        .codec_name = "snd-soc-dummy",
        .trigger = {SND_SOC_DPCM_TRIGGER_POST,
            SND_SOC_DPCM_TRIGGER_POST},
        .ignore_suspend = 1,
        /* this dainlink has playback support */
        .ignore_pmdown_time = 1,
        .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
    },
    {/* hw:x,2 */
        .name = "Circuit-Switch Voice",
        .stream_name = "CS-Voice",
        .cpu_dai_name   = "CS-VOICE",
        .platform_name  = "msm-pcm-voice",
        .dynamic = 1,
        .dpcm_playback = 1,
        .dpcm_capture = 1,
        .trigger = {SND_SOC_DPCM_TRIGGER_POST,
            SND_SOC_DPCM_TRIGGER_POST},
        .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
        .ignore_suspend = 1,
        /* this dainlink has playback support */
        .ignore_pmdown_time = 1,
        .be_id = MSM_FRONTEND_DAI_CS_VOICE,
        .codec_dai_name = "snd-soc-dummy-dai",
        .codec_name = "snd-soc-dummy",
    },
    ...
}

猜你喜欢

转载自www.cnblogs.com/helloworldtoyou/p/10499787.html