ASoC被分为Machine、Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。

ASoC的一切都从Machine驱动开始,包括声卡的注册,绑定Platform和Codec驱动等等;(linux内核版本为3.10.28)

1. 注册Platform driver:

ASoC把声卡注册为Platform Device:

static int msm8x16_asoc_machine_probe(struct platform_device *pdev)

{

struct snd_soc_card *card;

struct msm8916_asoc_mach_data *pdata = NULL;

struct pinctrl *pinctrl;

const char *card_dev_id = "qcom,msm-snd-card-id";

const char *codec_type = "qcom,msm-codec-type";

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 *spk_ext_pa = "qcom,msm-spk-ext-pa";

const char *ptr = NULL;

const char *type = NULL;

const char *ext_pa_str = NULL;

int num_strings;

int ret, id, i;

pr_err("'msm8x16_asoc_machine_probe ......");

pdata = devm_kzalloc(&pdev->dev,

sizeof(struct msm8916_asoc_mach_data), GFP_KERNEL);

if (!pdata) {

dev_err(&pdev->dev, "Can't allocate msm8x16_asoc_mach_data\n");

ret = -ENOMEM;

goto err1;

}

pdata->vaddr_gpio_mux_spkr_ctl =

ioremap(LPASS_CSR_GP_IO_MUX_SPKR_CTL , );

if (!pdata->vaddr_gpio_mux_spkr_ctl) {

pr_err("%s ioremap failure for addr %x",

__func__, LPASS_CSR_GP_IO_MUX_SPKR_CTL);

ret = -ENOMEM;

goto err;

}

pdata->vaddr_gpio_mux_mic_ctl =

ioremap(LPASS_CSR_GP_IO_MUX_MIC_CTL , );

if (!pdata->vaddr_gpio_mux_mic_ctl) {

pr_err("%s ioremap failure for addr %x",

__func__, LPASS_CSR_GP_IO_MUX_MIC_CTL);

ret = -ENOMEM;

goto err;

}

pdata->vaddr_gpio_mux_pcm_ctl =

ioremap(LPASS_CSR_GP_LPAIF_PRI_PCM_PRI_MODE_MUXSEL, );

if (!pdata->vaddr_gpio_mux_pcm_ctl) {

pr_err("%s ioremap failure for addr %x",

__func__,

LPASS_CSR_GP_LPAIF_PRI_PCM_PRI_MODE_MUXSEL);

ret = -ENOMEM;

goto err;

}

ret = of_property_read_u32(pdev->dev.of_node, card_dev_id, &id);

if (ret) {

dev_err(&pdev->dev,

"%s: missing %s in dt node\n", __func__, card_dev_id);

goto err;

}

pdev->id = id;

if (!pdev->dev.of_node) {

dev_err(&pdev->dev, "No platform supplied from device tree\n");

ret = -EINVAL;

goto err;

}

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;

pdata->spk_ext_pa_gpio = of_get_named_gpio(pdev->dev.of_node,

spk_ext_pa, );

if (pdata->spk_ext_pa_gpio < ) {

dev_dbg(&pdev->dev,

"%s: missing %s in dt node\n", __func__, spk_ext_pa);

} else {

if (gpio_is_valid(pdata->spk_ext_pa_gpio)) {

ret = gpio_request(pdata->spk_ext_pa_gpio, "spk_ext_pa_gpio");

if(ret) {

pr_err("spk ext pa gpio request failed");

goto err;

}

ret = gpio_direction_output(pdata->spk_ext_pa_gpio, );

if(ret) {

pr_err("set_direction for spk ext pa gpio failed\n");

goto err;

}

} else {

pr_err("%s: Invaild external_speaker gpio: %d", __func__, pdata->spk_ext_pa_gpio);

ret = -EINVAL;

goto err;

}

}

ret = of_property_read_string(pdev->dev.of_node, codec_type, &ptr);

if (ret) {

dev_err(&pdev->dev,

"%s: missing %s in dt node\n", __func__, codec_type);

goto err;

}

if (pdev->id >= MAX_SND_CARDS) {

dev_err(&pdev->dev, "Sound Card parsed is wrong, id=%d\n",

pdev->id);

ret = -EINVAL;

goto err;

}

if (!strcmp(ptr, "external")) {

dev_info(&pdev->dev, "external codec is configured\n");

pdata->codec_type = ;

/*Populate external codec TLMM configs*/

ret = populate_ext_snd_card_dt_data(pdev);

if (ret < ) {

dev_err(&pdev->dev, "error finding the DT\n"

"params ret=%d\n", ret);

goto err;

}

card = populate_ext_snd_card_dailinks(pdev);

if (!card) {

dev_err(&pdev->dev, "%s: Card uninitialized\n",

__func__);

ret = -EPROBE_DEFER;

goto err;

}

} else {

card = populate_ext_snd_card_dailinks(pdev);

if (!card) {

dev_err(&pdev->dev, "%s: Card uninitialized\n",

__func__);

ret = -EPROBE_DEFER;

goto err;

}

dev_info(&pdev->dev, "default codec configured\n");

pdata->codec_type = ;

num_strings = of_property_count_strings(pdev->dev.of_node,

ext_pa);

if (num_strings < ) {

dev_err(&pdev->dev,

"%s: missing %s in dt node or length is incorrect\n",

__func__, ext_pa);

goto err;

}

for (i = ; 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);

}

pr_debug("%s: ext_pa = %d\n", __func__, pdata->ext_pa);

pinctrl = devm_pinctrl_get(&pdev->dev);

if (IS_ERR(pinctrl)) {

pr_err("%s: Unable to get pinctrl handle\n",

__func__);

return -EINVAL;

}

pinctrl_info.pinctrl = pinctrl;

ret = get_cdc_gpio_lines(pinctrl, pdata->ext_pa);

if (ret < ) {

pr_err("%s: failed to ger the codec gpio's %d\n",

__func__, ret);

goto err;

}

}

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;

}

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;

}

/* 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 = ;

pdata->digital_cdc_clk.reserved = ;

/* 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 = ;

/* Initialize loopback mode to false */

pdata->lb_mode = false;

msm8x16_setup_hs_jack(pdev, pdata);

msm8x16_dt_parse_cap_info(pdev, pdata);

card->dev = &pdev->dev;

platform_set_drvdata(pdev, card);

snd_soc_card_set_drvdata(card, pdata);

ret = snd_soc_of_parse_card_name(card, "qcom,model");

if (ret)

goto err;

/* initialize timer */

INIT_DELAYED_WORK(&pdata->disable_mclk_work, disable_mclk);

mutex_init(&pdata->cdc_mclk_mutex);

atomic_set(&pdata->mclk_rsc_ref, );

atomic_set(&pdata->mclk_enabled, false);

atomic_set(&quat_mi2s_clk_ref, );

atomic_set(&auxpcm_mi2s_clk_ref, );

ret = snd_soc_of_parse_audio_routing(card,

"qcom,audio-routing");

if (ret)

goto err;

ret = msm8x16_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;

}

ret = core_get_adsp_ver();

if (ret < ) {

ret = -EPROBE_DEFER;

dev_info(&pdev->dev, "%s: Get adsp version failed (%d)\n",

__func__, ret);

goto err;

}

return ;

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(gpio_is_valid(pdata->spk_ext_pa_gpio))

gpio_free(pdata->spk_ext_pa_gpio);

devm_kfree(&pdev->dev, pdata);

err1:

return ret;

}

probe函数

DTS:

sound {

compatible = "qcom,msm8x16-audio-codec";

qcom,model = "msm8x16-skui-snd-card";

qcom,msm-snd-card-id = <>;

qcom,msm-ext-pa = "secondary";//"primary";

qcom,msm-codec-type = "internal";

qcom,msm-mbhc-hphl-swh = <>;

qcom,msm-mbhc-gnd-swh = <>;

qcom,msm-hs-micbias-type = "internal";

qcom,audio-routing =

"RX_BIAS", "MCLK",

"SPK_RX_BIAS", "MCLK",

"INT_LDO_H", "MCLK",

"MIC BIAS Internal1", "Handset Mic",

"MIC BIAS Internal2", "Headset Mic",

"MIC BIAS Internal1", "Secondary Mic",

"AMIC1", "MIC BIAS Internal1",

"AMIC2", "MIC BIAS Internal2",

"AMIC3", "MIC BIAS Internal1";

pinctrl-names = "cdc_lines_act",

"cdc_lines_sus",

"cdc_lines_sec_ext_act",

"cdc_lines_sec_ext_sus";

pinctrl- = ;

pinctrl- = ;

pinctrl- = ;

pinctrl- = ;

};

通过与DTS匹配,开始分析:

(1)、获取card的id:

ret = of_property_read_u32(pdev->dev.of_node, card_dev_id, &id);

(2)、设置card的名字:

pdev->id = id;

2 dev_set_name(&pdev->dev, "%s.%d", "msm-snd-card", id);

(3)、设置codec的类型为external还是internal的:

ret = of_property_read_string(pdev->dev.of_node, codec_type, &ptr);

if (ret) {

dev_err(&pdev->dev,

"%s: missing %s in dt node\n", __func__, codec_type);

goto err;

}

(4)、根据external还是internal的card,进入相应的处理函数中:

假设进入internal card:

card = &bear_cards[pdev->id];

bear_cards[pdev->id].name = dev_name(&pdev->dev);

在这里,bear_cards是一个snd_soc_card的结构体,由设备树又可知,id=0:

static struct snd_soc_card bear_cards[MAX_SND_CARDS] = {

/* snd_soc_card_msm8x16 */

{

.name = "msm8x16-snd-card",

.dai_link = msm8x16_dai,

.num_links = ARRAY_SIZE(msm8x16_dai),

},

{

.name = "msm8x16-tapan-snd-card",

.dai_link = msm8x16_9306_dai_links,

.num_links = ARRAY_SIZE(msm8x16_9306_dai_links),

},

{

.name = "msm8x16-tapan9302-snd-card",

.dai_link = msm8x16_9302_dai_links,

.num_links = ARRAY_SIZE(msm8x16_9302_dai_links),

},

};

所以用到的只有msm8x16_dai;

/* Digital audio interface glue - connects codec CPU */

static struct snd_soc_dai_link msm8x16_dai[] = {

/* FrontEnd DAI Links */

{/* hw:x,0 */

.name = "MSM8X16 Media1",

.stream_name = "MultiMedia1",

.cpu_dai_name = "MultiMedia1",

.platform_name = "msm-pcm-dsp.0",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.ignore_suspend = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1

},

{/* hw:x,1 */

.name = "MSM8X16 Media2",

.stream_name = "MultiMedia2",

.cpu_dai_name = "MultiMedia2",

.platform_name = "msm-pcm-dsp.0",

.dynamic = ,

.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 = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.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 = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.be_id = MSM_FRONTEND_DAI_CS_VOICE,

},

{/* hw:x,3 */

.name = "MSM VoIP",

.stream_name = "VoIP",

.cpu_dai_name = "VoIP",

.platform_name = "msm-voip-dsp",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.ignore_suspend = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.be_id = MSM_FRONTEND_DAI_VOIP,

},

{/* hw:x,4 */

.name = "MSM8X16 LPA",

.stream_name = "LPA",

.cpu_dai_name = "MultiMedia3",

.platform_name = "msm-pcm-lpa",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.ignore_suspend = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,

},

/* Hostless PCM purpose */

{/* hw:x,5 */

.name = "Primary MI2S_RX Hostless",

.stream_name = "Primary MI2S_RX Hostless",

.cpu_dai_name = "PRI_MI2S_RX_HOSTLESS",

.platform_name = "msm-pcm-hostless",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

.ignore_pmdown_time = ,

/* This dainlink has MI2S support */

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

},

{/* hw:x,6 */

.name = "INT_FM Hostless",

.stream_name = "INT_FM Hostless",

.cpu_dai_name = "INT_FM_HOSTLESS",

.platform_name = "msm-pcm-hostless",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

},

{/* hw:x,7 */

.name = "MSM AFE-PCM RX",

.stream_name = "AFE-PROXY RX",

.cpu_dai_name = "msm-dai-q6-dev.241",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-rx",

.platform_name = "msm-pcm-afe",

.ignore_suspend = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

},

{/* hw:x,8 */

.name = "MSM AFE-PCM TX",

.stream_name = "AFE-PROXY TX",

.cpu_dai_name = "msm-dai-q6-dev.240",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-tx",

.platform_name = "msm-pcm-afe",

.ignore_suspend = ,

},

{/* hw:x,9 */

.name = "MSM8X16 Compr",

.stream_name = "COMPR",

.cpu_dai_name = "MultiMedia4",

.platform_name = "msm-compress-dsp",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.ignore_suspend = ,

.ignore_pmdown_time = ,

/* this dainlink has playback support */

.be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,

},

{/* hw:x,10 */

.name = "AUXPCM Hostless",

.stream_name = "AUXPCM Hostless",

.cpu_dai_name = "AUXPCM_HOSTLESS",

.platform_name = "msm-pcm-hostless",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

},

{/* hw:x,11 */

.name = "Tertiary MI2S_TX Hostless",

.stream_name = "Tertiary MI2S_TX Hostless",

.cpu_dai_name = "TERT_MI2S_TX_HOSTLESS",

.platform_name = "msm-pcm-hostless",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

.ignore_pmdown_time = , /* dai link has playback support */

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

},

{/* hw:x,12 */

.name = "MSM8x16 LowLatency",

.stream_name = "MultiMedia5",

.cpu_dai_name = "MultiMedia5",

.platform_name = "msm-pcm-dsp.1",

.dynamic = ,

.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 = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,

},

{/* hw:x,13 */

.name = "Voice2",

.stream_name = "Voice2",

.cpu_dai_name = "Voice2",

.platform_name = "msm-pcm-voice",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

},

{/* hw:x,14 */

.name = "MSM8x16 Media9",

.stream_name = "MultiMedia9",

.cpu_dai_name = "MultiMedia9",

.platform_name = "msm-pcm-dsp.0",

.dynamic = ,

.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 = ,

/* This dailink has playback support */

.ignore_pmdown_time = ,

.be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,

},

{ /* hw:x,15 */

.name = "VoLTE",

.stream_name = "VoLTE",

.cpu_dai_name = "VoLTE",

.platform_name = "msm-pcm-voice",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.be_id = MSM_FRONTEND_DAI_VOLTE,

},

{ /* hw:x,16 */

.name = "VoWLAN",

.stream_name = "VoWLAN",

.cpu_dai_name = "VoWLAN",

.platform_name = "msm-pcm-voice",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.be_id = MSM_FRONTEND_DAI_VOWLAN,

},

{/* hw:x,17 */

.name = "INT_HFP_BT Hostless",

.stream_name = "INT_HFP_BT Hostless",

.cpu_dai_name = "INT_HFP_BT_HOSTLESS",

.platform_name = "msm-pcm-hostless",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST},

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

/* this dai link has playback support */

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

},

{/* hw:x,18 */

.name = "MSM8916 HFP TX",

.stream_name = "MultiMedia6",

.cpu_dai_name = "MultiMedia6",

.platform_name = "msm-pcm-loopback",

.dynamic = ,

.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 = ,

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

/* this dai link has playback support */

.ignore_pmdown_time = ,

.be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,

},

/* LSM FE */

{/* hw:x,19 */

.name = "Listen 1 Audio Service",

.stream_name = "Listen 1 Audio Service",

.cpu_dai_name = "LSM1",

.platform_name = "msm-lsm-client",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST },

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.be_id = MSM_FRONTEND_DAI_LSM1,

},

{/* hw:x,20 */

.name = "Listen 2 Audio Service",

.stream_name = "Listen 2 Audio Service",

.cpu_dai_name = "LSM2",

.platform_name = "msm-lsm-client",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST },

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.be_id = MSM_FRONTEND_DAI_LSM2,

},

{/* hw:x,21 */

.name = "Listen 3 Audio Service",

.stream_name = "Listen 3 Audio Service",

.cpu_dai_name = "LSM3",

.platform_name = "msm-lsm-client",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST },

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.be_id = MSM_FRONTEND_DAI_LSM3,

},

{/* hw:x,22 */

.name = "Listen 4 Audio Service",

.stream_name = "Listen 4 Audio Service",

.cpu_dai_name = "LSM4",

.platform_name = "msm-lsm-client",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST },

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.be_id = MSM_FRONTEND_DAI_LSM4,

},

{/* hw:x,23 */

.name = "Listen 5 Audio Service",

.stream_name = "Listen 5 Audio Service",

.cpu_dai_name = "LSM5",

.platform_name = "msm-lsm-client",

.dynamic = ,

.trigger = {SND_SOC_DPCM_TRIGGER_POST,

SND_SOC_DPCM_TRIGGER_POST },

.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,

.ignore_suspend = ,

.ignore_pmdown_time = ,

.codec_dai_name = "snd-soc-dummy-dai",

.codec_name = "snd-soc-dummy",

.be_id = MSM_FRONTEND_DAI_LSM5,

},

{/* hw:x,24 */

.name = "MSM8916 ULL",

.stream_name = "MultiMedia7",

.cpu_dai_name = "MultiMedia7",

.platform_name = "msm-pcm-dsp.1",

.dynamic = ,

.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 = ,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,

},

/* Backend I2S DAI Links */

{

.name = LPASS_BE_PRI_MI2S_RX,

.stream_name = "Primary MI2S Playback",

.cpu_dai_name = "msm-dai-q6-mi2s.0",

.platform_name = "msm-pcm-routing",

.codec_name = MSM8X16_CODEC_NAME,

.codec_dai_name = "msm8x16_wcd_i2s_rx1",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,

.init = &msm_audrx_init,

.be_hw_params_fixup = msm_pri_rx_be_hw_params_fixup,

.ops = &msm8x16_mi2s_be_ops,

.ignore_suspend = ,

},

{

.name = LPASS_BE_SEC_MI2S_RX,

.stream_name = "Secondary MI2S Playback",

.cpu_dai_name = "msm-dai-q6-mi2s.1",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-rx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,

.be_hw_params_fixup = msm_be_hw_params_fixup,

.ops = &msm8x16_sec_mi2s_be_ops,

.ignore_suspend = ,

},

{

.name = LPASS_BE_TERT_MI2S_TX,

.stream_name = "Tertiary MI2S Capture",

.cpu_dai_name = "msm-dai-q6-mi2s.2",

.platform_name = "msm-pcm-routing",

.codec_name = MSM8X16_CODEC_NAME,

.codec_dai_name = "msm8x16_wcd_i2s_tx1",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,

.be_hw_params_fixup = msm_tx_be_hw_params_fixup,

.ops = &msm8x16_mi2s_be_ops,

.ignore_suspend = ,

},

{

.name = LPASS_BE_INT_BT_SCO_RX,

.stream_name = "Internal BT-SCO Playback",

.cpu_dai_name = "msm-dai-q6-dev.12288",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-rx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,

.be_hw_params_fixup = msm_btsco_be_hw_params_fixup,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.ignore_suspend = ,

},

{

.name = LPASS_BE_INT_BT_SCO_TX,

.stream_name = "Internal BT-SCO Capture",

.cpu_dai_name = "msm-dai-q6-dev.12289",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-tx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,

.be_hw_params_fixup = msm_btsco_be_hw_params_fixup,

.ignore_suspend = ,

},

{

.name = LPASS_BE_INT_FM_RX,

.stream_name = "Internal FM Playback",

.cpu_dai_name = "msm-dai-q6-dev.12292",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-rx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_INT_FM_RX,

.be_hw_params_fixup = msm_be_hw_params_fixup,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.ignore_suspend = ,

},

{

.name = LPASS_BE_INT_FM_TX,

.stream_name = "Internal FM Capture",

.cpu_dai_name = "msm-dai-q6-dev.12293",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-tx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_INT_FM_TX,

.be_hw_params_fixup = msm_be_hw_params_fixup,

.ignore_suspend = ,

},

{

.name = LPASS_BE_AFE_PCM_RX,

.stream_name = "AFE Playback",

.cpu_dai_name = "msm-dai-q6-dev.224",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-rx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_AFE_PCM_RX,

.be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup,

/* this dainlink has playback support */

.ignore_pmdown_time = ,

.ignore_suspend = ,

},

{

.name = LPASS_BE_AFE_PCM_TX,

.stream_name = "AFE Capture",

.cpu_dai_name = "msm-dai-q6-dev.225",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-tx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,

.be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,

.ignore_suspend = ,

},

/* Incall Record Uplink BACK END DAI Link */

{

.name = LPASS_BE_INCALL_RECORD_TX,

.stream_name = "Voice Uplink Capture",

.cpu_dai_name = "msm-dai-q6-dev.32772",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-tx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,

.be_hw_params_fixup = msm_be_hw_params_fixup,

.ignore_suspend = ,

},

/* Incall Record Downlink BACK END DAI Link */

{

.name = LPASS_BE_INCALL_RECORD_RX,

.stream_name = "Voice Downlink Capture",

.cpu_dai_name = "msm-dai-q6-dev.32771",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-tx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,

.be_hw_params_fixup = msm_be_hw_params_fixup,

.ignore_suspend = ,

},

/* Incall Music BACK END DAI Link */

{

.name = LPASS_BE_VOICE_PLAYBACK_TX,

.stream_name = "Voice Farend Playback",

.cpu_dai_name = "msm-dai-q6-dev.32773",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-rx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,

.be_hw_params_fixup = msm_be_hw_params_fixup,

.ignore_suspend = ,

},

/* Incall Music 2 BACK END DAI Link */

{

.name = LPASS_BE_VOICE2_PLAYBACK_TX,

.stream_name = "Voice2 Farend Playback",

.cpu_dai_name = "msm-dai-q6-dev.32770",

.platform_name = "msm-pcm-routing",

.codec_name = "msm-stub-codec.1",

.codec_dai_name = "msm-stub-rx",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,

.be_hw_params_fixup = msm_be_hw_params_fixup,

.ignore_suspend = ,

},

};

snd_dai_link msm8x16_dai

其中,snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字,稍后Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的,这样看来,Machine驱动的设备初始化代码无非就是选择合适Platform和Codec以及dai,用他们填充以上几个数据结构,然后注册Platform设备即可。

当然还要实现连接Platform和Codec的dai_link对应的ops实现;

msm8x16_sec_mi2s_be_ops的结构体是一个函数指针结构体,里面注册了相应的回调函数:

1 static struct snd_soc_ops msm8x16_mi2s_be_ops = {

2 .startup = msm_mi2s_snd_startup,

3 .hw_params = msm_mi2s_snd_hw_params,

4 .shutdown = msm_mi2s_snd_shutdown,

5 };

在高通平台中,这primary_mi2s这一路i2s,都是留给内部codec用的,所以,这路的dai_link上的codec_name和codec_dai_name,就是对应着内部codec的信息:

/* Backend I2S DAI Links */

{

.name = LPASS_BE_PRI_MI2S_RX,

.stream_name = "Primary MI2S Playback",

.cpu_dai_name = "msm-dai-q6-mi2s.0",

.platform_name = "msm-pcm-routing",

.codec_name = MSM8X16_CODEC_NAME,

.codec_dai_name = "msm8x16_wcd_i2s_rx1",

.no_pcm = ,

.be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,

.init = &msm_audrx_init,

.be_hw_params_fixup = msm_pri_rx_be_hw_params_fixup,

.ops = &msm8x16_mi2s_be_ops,

.ignore_suspend = ,

},

从msm8x16_wcd_i2s_rx1我们便可以找到高通平台默认的msm8x16-wcd.c,在该文件中,注册了snd_soc_codec_driver:

(5)、匹配并注册相应的驱动:

如何匹配?

那这里就要谈论一个问题,在初始化的时候,如何凭借dai_link中的codec信息找到对应的codec,答案是codec_name。但注意,这里并不是通过这个名字直接寻找的,例如8916平台。

这下就来到重要的函数:

ret = snd_soc_register_card(card);

if (ret) {

dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",

ret);

goto err;

}

/**

* snd_soc_register_card - Register a card with the ASoC core

*

* @card: Card to register

*

*/

int snd_soc_register_card(struct snd_soc_card *card)

{

int i, ret;

if (!card->name || !card->dev)

return -EINVAL;

for (i = ; i < card->num_links; i++) {

struct snd_soc_dai_link *link = &card->dai_link[i];

/*

* Codec must be specified by 1 of name or OF node,

* not both or neither.

*/

if (!!link->codec_name == !!link->codec_of_node) {

dev_err(card->dev, "ASoC: Neither/both codec"

" name/of_node are set for %s\n", link->name);

return -EINVAL;

}

/* Codec DAI name must be specified */

if (!link->codec_dai_name) {

dev_err(card->dev, "ASoC: codec_dai_name not"

" set for %s\n", link->name);

return -EINVAL;

}

/*

* Platform may be specified by either name or OF node, but

* can be left unspecified, and a dummy platform will be used.

*/

if (link->platform_name && link->platform_of_node) {

dev_err(card->dev, "ASoC: Both platform name/of_node"

" are set for %s\n", link->name);

return -EINVAL;

}

/*

* CPU device may be specified by either name or OF node, but

* can be left unspecified, and will be matched based on DAI

* name alone..

*/

if (link->cpu_name && link->cpu_of_node) {

dev_err(card->dev, "ASoC: Neither/both "

"cpu name/of_node are set for %s\n",link->name);

return -EINVAL;

}

/*

* At least one of CPU DAI name or CPU device name/node must be

* specified

*/

if (!link->cpu_dai_name &&

!(link->cpu_name || link->cpu_of_node)) {

dev_err(card->dev, "ASoC: Neither cpu_dai_name nor "

"cpu_name/of_node are set for %s\n", link->name);

return -EINVAL;

}

}

dev_set_drvdata(card->dev, card);

snd_soc_initialize_card_lists(card);

soc_init_card_debugfs(card);

card->rtd = devm_kzalloc(card->dev,

sizeof(struct snd_soc_pcm_runtime) *

(card->num_links + card->num_aux_devs),

GFP_KERNEL);

if (card->rtd == NULL)

return -ENOMEM;

card->num_rtd = ;

card->rtd_aux = &card->rtd[card->num_links];

for (i = ; i < card->num_links; i++)

card->rtd[i].dai_link = &card->dai_link[i];

INIT_LIST_HEAD(&card->list);

INIT_LIST_HEAD(&card->dapm_dirty);

card->instantiated = ;

mutex_init(&card->mutex);

mutex_init(&card->dapm_mutex);

mutex_init(&card->dapm_power_mutex);

ret = snd_soc_instantiate_card(card);

if (ret != )

soc_cleanup_card_debugfs(card);

return ret;

}

snd_soc_register_card

将link指针遍历msm8x16_dai结构体数组的每一个成员:

for (i = ; i < card->num_links; i++) {

struct snd_soc_dai_link *link = &card->dai_link[i];

这里定义了codec_name,名字为msm8x16_wcd_codec,所以不执行if的内容:

if (!!link->codec_name == !!link->codec_of_node) {

dev_err(card->dev, "ASoC: Neither/both codec"

" name/of_node are set for %s\n", link->name);

return -EINVAL;

}

同理,下面的函数也是一样:

/* Codec DAI name must be specified */

if (!link->codec_dai_name) {

dev_err(card->dev, "ASoC: codec_dai_name not"

" set for %s\n", link->name);

return -EINVAL;

}

/*

* Platform may be specified by either name or OF node, but

* can be left unspecified, and a dummy platform will be used.

*/

if (link->platform_name && link->platform_of_node) {

dev_err(card->dev, "ASoC: Both platform name/of_node"

" are set for %s\n", link->name);

return -EINVAL;

}

/*

* CPU device may be specified by either name or OF node, but

* can be left unspecified, and will be matched based on DAI

* name alone..

*/

if (link->cpu_name && link->cpu_of_node) {

dev_err(card->dev, "ASoC: Neither/both "

"cpu name/of_node are set for %s\n",link->name);

return -EINVAL;

}

/*

* At least one of CPU DAI name or CPU device name/node must be

* specified

*/

if (!link->cpu_dai_name &&

!(link->cpu_name || link->cpu_of_node)) {

dev_err(card->dev, "ASoC: Neither cpu_dai_name nor "

"cpu_name/of_node are set for %s\n", link->name);

return -EINVAL;

}

继续向下看:

//设置声卡设备驱动信息

dev_set_drvdata(card->dev,card);

//初始化声卡设备列表

snd_soc_initialize_card_lists(card);

soc_init_card_debugfs(card);

//为声卡中的snd_soc_pcm_runtime数据结构分配内存空间

card->rtd= devm_kzalloc(card->dev,

sizeof(struct snd_soc_pcm_runtime) *

(card->num_links + card->num_aux_devs),

GFP_KERNEL);

if(card->rtd == NULL)

return-ENOMEM;

card->num_rtd= ;

card->rtd_aux= &card->rtd[card->num_links];

for(i = ; i < card->num_links; i++)

card->rtd[i].dai_link= &card->dai_link[i];

INIT_LIST_HEAD(&card->list);

INIT_LIST_HEAD(&card->dapm_dirty);

card->instantiated= ; //表明声卡还没有被初始化

mutex_init(&card->mutex);

mutex_init(&card->dapm_mutex);

//初始化声卡

ret= snd_soc_instantiate_card(card);

if(ret != )

soc_cleanup_card_debugfs(card);

在snd_soc_instantiate_card函数中:

static int snd_soc_instantiate_card(struct snd_soc_card *card)

{

struct snd_soc_codec *codec;

struct snd_soc_codec_conf *codec_conf;

enum snd_soc_compress_type compress_type;

struct snd_soc_dai_link *dai_link;

int ret, i, order, dai_fmt;

mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);

/* bind DAIs */

for (i = ; i < card->num_links; i++) {

ret = soc_bind_dai_link(card, i);

if (ret != )

goto base_error;

}

/* check aux_devs too */

for (i = ; i < card->num_aux_devs; i++) {

ret = soc_check_aux_dev(card, i);

if (ret != )

goto base_error;

}

/* initialize the register cache for each available codec */

list_for_each_entry(codec, &codec_list, list) {

if (codec->cache_init)

continue;

/* by default we don't override the compress_type */

compress_type = ;

/* check to see if we need to override the compress_type */

for (i = ; i < card->num_configs; ++i) {

codec_conf = &card->codec_conf[i];

if (!strcmp(codec->name, codec_conf->dev_name)) {

compress_type = codec_conf->compress_type;

if (compress_type && compress_type

!= codec->compress_type)

break;

}

}

ret = snd_soc_init_codec_cache(codec, compress_type);

if (ret < )

goto base_error;

}

/* card bind complete so register a sound card */

ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,

card->owner, , &card->snd_card);

if (ret < ) {

dev_err(card->dev, "ASoC: can't create sound card for"

" card %s: %d\n", card->name, ret);

goto base_error;

}

card->snd_card->dev = card->dev;

card->dapm.bias_level = SND_SOC_BIAS_OFF;

card->dapm.dev = card->dev;

card->dapm.card = card;

list_add(&card->dapm.list, &card->dapm_list);

#ifdef CONFIG_DEBUG_FS

snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);

#endif

#ifdef CONFIG_PM_SLEEP

/* deferred resume work */

INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);

#endif

if (card->dapm_widgets)

snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,

card->num_dapm_widgets);

/* initialise the sound card only once */

if (card->probe) {

ret = card->probe(card);

if (ret < )

goto card_probe_error;

}

/* probe all components used by DAI links on this card */

for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;

order++) {

for (i = ; i < card->num_links; i++) {

ret = soc_probe_link_components(card, i, order);

if (ret < ) {

dev_err(card->dev,

"ASoC: failed to instantiate card %d\n",

ret);

goto probe_dai_err;

}

}

}

/* probe all DAI links on this card */

for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;

order++) {

for (i = ; i < card->num_links; i++) {

ret = soc_probe_link_dais(card, i, order);

if (ret < ) {

dev_err(card->dev,

"ASoC: failed to instantiate card %d\n",

ret);

goto probe_dai_err;

}

}

}

for (i = ; i < card->num_aux_devs; i++) {

ret = soc_probe_aux_dev(card, i);

if (ret < ) {

dev_err(card->dev,

"ASoC: failed to add auxiliary devices %d\n",

ret);

goto probe_aux_dev_err;

}

}

snd_soc_dapm_link_dai_widgets(card);

if (card->controls)

snd_soc_add_card_controls(card, card->controls, card->num_controls);

if (card->dapm_routes)

snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,

card->num_dapm_routes);

snd_soc_dapm_new_widgets(&card->dapm);

for (i = ; i < card->num_links; i++) {

dai_link = &card->dai_link[i];

dai_fmt = dai_link->dai_fmt;

if (dai_fmt) {

ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,

dai_fmt);

if (ret != && ret != -ENOTSUPP)

dev_warn(card->rtd[i].codec_dai->dev,

"ASoC: Failed to set DAI format: %d\n",

ret);

}

/* If this is a regular CPU link there will be a platform */

if (dai_fmt &&

(dai_link->platform_name || dai_link->platform_of_node)) {

ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,

dai_fmt);

if (ret != && ret != -ENOTSUPP)

dev_warn(card->rtd[i].cpu_dai->dev,

"ASoC: Failed to set DAI format: %d\n",

ret);

} else if (dai_fmt) {

/* Flip the polarity for the "CPU" end */

dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;

switch (dai_link->dai_fmt &

SND_SOC_DAIFMT_MASTER_MASK) {

case SND_SOC_DAIFMT_CBM_CFM:

dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;

break;

case SND_SOC_DAIFMT_CBM_CFS:

dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;

break;

case SND_SOC_DAIFMT_CBS_CFM:

dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;

break;

case SND_SOC_DAIFMT_CBS_CFS:

dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;

break;

}

ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,

dai_fmt);

if (ret != && ret != -ENOTSUPP)

dev_warn(card->rtd[i].cpu_dai->dev,

"ASoC: Failed to set DAI format: %d\n",

ret);

}

}

snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),

"%s", card->name);

snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),

"%s", card->long_name ? card->long_name : card->name);

snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),

"%s", card->driver_name ? card->driver_name : card->name);

for (i = ; i < ARRAY_SIZE(card->snd_card->driver); i++) {

switch (card->snd_card->driver[i]) {

case '_':

case '-':

case '\0':

break;

default:

if (!isalnum(card->snd_card->driver[i]))

card->snd_card->driver[i] = '_';

break;

}

}

if (card->late_probe) {

ret = card->late_probe(card);

if (ret < ) {

dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n",

card->name, ret);

goto probe_aux_dev_err;

}

}

snd_soc_dapm_new_widgets(&card->dapm);

if (card->fully_routed)

list_for_each_entry(codec, &card->codec_dev_list, card_list)

snd_soc_dapm_auto_nc_codec_pins(codec);

ret = snd_card_register(card->snd_card);

if (ret < ) {

dev_err(card->dev, "ASoC: failed to register soundcard %d\n",

ret);

goto probe_aux_dev_err;

}

#ifdef CONFIG_SND_SOC_AC97_BUS

/* register any AC97 codecs */

for (i = ; i < card->num_rtd; i++) {

ret = soc_register_ac97_dai_link(&card->rtd[i]);

if (ret < ) {

dev_err(card->dev, "ASoC: failed to register AC97:"

" %d\n", ret);

while (--i >= )

soc_unregister_ac97_dai_link(card->rtd[i].codec);

goto probe_aux_dev_err;

}

}

#endif

card->instantiated = ;

snd_soc_dapm_sync(&card->dapm);

mutex_unlock(&card->mutex);

return ;

probe_aux_dev_err:

for (i = ; i < card->num_aux_devs; i++)

soc_remove_aux_dev(card, i);

probe_dai_err:

soc_remove_dai_links(card);

card_probe_error:

if (card->remove)

card->remove(card);

snd_card_free(card->snd_card);

base_error:

mutex_unlock(&card->mutex);

return ret;

}

snd_soc_instantiate_card

/*bind DAIs */

for(i = ; i < card->num_links; i++) {

//逐一绑定声卡的各类DAI链接,下面会详细介绍该函数

ret= soc_bind_dai_link(card, i);

if(ret != )

gotobase_error;

}

/* initialize the register cache for each available codec */

//遍历CODEC列表中的所有CODEC

list_for_each_entry(codec, &codec_list, list) {

//CODEC缓存是否已初始化

if (codec->cache_init)

continue;

/* by default we don't override the compress_type */

//设置压缩类型

compress_type = ;

/* check to see if we need to override the compress_type */

for (i = ; i < card->num_configs; ++i) {

codec_conf = &card->codec_conf[i];

if (!strcmp(codec->name, codec_conf->dev_name)) {

compress_type = codec_conf->compress_type;

if (compress_type && compress_type

!= codec->compress_type)

break;

}

}

/*初始化CODEC缓存,该函数最终调用sound/soc/soc-cache.c文件内的snd_soc_flat_cache_init函数,为缓存(reg_cache)开辟内存空间,并把codec->cache_init置为1*/

ret = snd_soc_init_codec_cache(codec, compress_type);

if (ret < )

goto base_error;

}

//创建声卡

ret= snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,

card->owner,, &card->snd_card);

继续看soc_bind_dai_link函数,通过msm8x16-wcd.c中probe的of_device_id就可以知道:

pm8916_tombak_dig: msm8x16_wcd_codec@f000{

compatible = "qcom,wcd-spmi";

reg = <0xf000 0x100>;

interrupt-parent = ;

interrupts = <0x1 0xf0 0x0>,

<0x1 0xf0 0x1>,

<0x1 0xf0 0x2>,

<0x1 0xf0 0x3>,

<0x1 0xf0 0x4>,

<0x1 0xf0 0x5>,

<0x1 0xf0 0x6>,

<0x1 0xf0 0x7>;

interrupt-names = "spk_cnp_int",

"spk_clip_int",

"spk_ocp_int",

"ins_rem_det1",

"but_rel_det",

"but_press_det",

"ins_rem_det",

"mbhc_int";

而这个node节点正式codec驱动的设备树节点。在soc_bind_dai_link()函数中,会做出如下处理:

/*注册codec的时候,会将所有注册的codec链接到codec_list中*/

list_for_each_entry(codec, &codec_list, list) {

if (dai_link->codec_of_node) {

/*根据设备数节点句柄进行匹配*/

if (codec->dev->of_node != dai_link->codec_of_node)

continue;

} else {

/*如果句柄为空,根据,codec_name进行匹配,在这里不会走这里,其实codec_name是 wcd-spmi-core.1*/

if (strcmp(codec->name, dai_link->codec_name))

continue;

}

rtd->codec = codec;

/*找到codec之后,根据codec_dai的名字找到对应的codec_dai*/

list_for_each_entry(codec_dai, &dai_list, list) {

if (codec->dev == codec_dai->dev &&

!strcmp(codec_dai->name,

dai_link->codec_dai_name)) {

rtd->codec_dai = codec_dai;

}

}

}

所以,我们可以根据dai_link中的codec_dai的名字或者codec名字来找到对应的codec驱动。

另外,当播放音频的时候需要打开一个外部pa,所以probe函数中有一部分是external pa的初始化:

(6)、获取external pa:(也就是放大器):

1   for (i = 0; i < num_strings; i++) {

2 ret = of_property_read_string_index(pdev->dev.of_node,

3 ext_pa, i, &ext_pa_str);

4 if (ret) {

5 dev_err(&pdev->dev, "%s:of read string %s i %d error %d\n",

6 __func__, ext_pa, i, ret);

7 goto err;

8 }

9 if (!strcmp(ext_pa_str, "primary"))

10 pdata->ext_pa = (pdata->ext_pa | PRI_MI2S_ID);

11 else if (!strcmp(ext_pa_str, "secondary"))

12 pdata->ext_pa = (pdata->ext_pa | SEC_MI2S_ID);

13 else if (!strcmp(ext_pa_str, "tertiary"))

14 pdata->ext_pa = (pdata->ext_pa | TER_MI2S_ID);

15 else if (!strcmp(ext_pa_str, "quaternary"))

16 pdata->ext_pa = (pdata->ext_pa | QUAT_MI2S_ID);

17 }

18 pr_err("%s: ext_pa = %d\n", __func__, pdata->ext_pa);

pinctrl_info.pinctrl = pinctrl;

ret = get_cdc_gpio_lines(pinctrl, pdata->ext_pa);    //获取gpio状态

if (ret < ) {

pr_err("%s: failed to ger the codec gpio's %d\n",

__func__, ret);

goto err;

}

可以根据高通手册来看,所以设备树上的配置为secondary:

qcom,msm-ext-pa = "secondary";//"primary";

2. 相应的资料:

其实以上便是linux3.10以上的audio内核machine架构,网上搜索相应资料便可找到;贴上借鉴的资料:

网上大牛的架构:

高通ASOC中的codec驱动

ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在Machine中已经知道,snd_soc_dai_link结构就指明 ...

高通Audio中ASOC的machine驱动(一)

ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...

高通LCD的pwm背光驱动

发生异常的现象: msm8953 lcd在快速亮灭的情况下背光概率性休眠不灭:测量高通pwm,发现正常的时候pwm的管脚LCM_BL_PWM为低电平,失败的时候为高电平: 根据原理图: mpp是什么? ...

【转】高通平台android 环境配置编译及开发经验总结

原文网址:http://blog.csdn.net/dongwuming/article/details/12784535 1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通 ...

高通android开发摘要

一部分是开源的,可以从codeaurora.org上下载,还有一部分是高通产权的,需要从高通的网站上下载. 将高通产权的代码放到:vendor/qcom/proprietary 1. 设置bms一些参 ...

高通adsp架构下sensor

一.高通sensor架构: linux驱动由浅入深系列:高通sensor架构实例分析之一(整体概览+AP侧代码分析) linux驱动由浅入深系列:高通sensor架构实例分析之二(adsp驱动代码结构 ...

高通、猎户机型Android典型bootloader分析

1.bootloader是什么? 简单地说,bootloader 就是在操作系统内核运行之前运行的一段小程序.通过这段小程序,我们可以初始化硬件设备.建立内存空间的映射图,从而将系统的软硬件环境带到一 ...

Linux ALSA声卡驱动之六:ASoC架构中的Machine

前面一节的内容我们提到,ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上 ...

linux驱动由浅入深系列:PBL-SBL1-&lpar;bootloader&rpar;LK-Android启动过程详解之一(高通MSM8953启动实例)

转自:http://blog.csdn.net/radianceblau/article/details/73229005 http://www.aiuxian.com/article/p-14142 ...

随机推荐

jdbc java数据库连接 10)批处理

批处理 很多时候,需要批量执行sql语句! 需求:批量保存信息! 设计: AdminDao Public  void  save(List

android加载大图片到内存

1)演示效果: 1)代码演示: 布局代码: 权限配置:

iOS项目的目录结构和开发流程(Cocoa China)

目录结构 AppDelegate Models Macro General Helpers Vendors Sections Resources   一个合理的目录结构首先应该是清晰的,让人一眼看上去 ...

php下载文件的代码示例

php下载文件的代码示例,需要的朋友可以参考下 <?php   $file = 'monkey.gif';  if (file_exists($file)) {  header('Content- ...

js学习笔记—转载(闭包问题)

---恢复内容开始--- 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.     一.变量的作用域 要理解闭包,首先必须理解Javascrip ...

sublime text格式化插件

sublime text 软件其实是自带格式化插件的,但是它默认的格式化插件,不太好用,且没有快捷键(虽然自己可以设置). 其默认的格式化是在 Edit  ->  Line  ->  Re ...

java创建多线程(转载)

转载自:Java创建线程的两个方法 Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对 ...

【hihoCoder】&num;1039 &colon; 字符消除 by C solution

#1039 : 字符消除 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi最近在玩一个字符消除游戏.给定一个只包含大写字母"ABC"的字符串s,消 ...

Centos Git1&period;7&period;1升级到Git2&period;2&period;1

安装需求: ># yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel asciidoc ># ...

afe 高通_高通ASOC中的machine驱动相关推荐

  1. eclipse经常高占用_高可用系统的设计指南

    可靠的系统是业务稳定.快速发展的基石.那么,如何做到系统高可靠.高可用呢?下面从技术方面介绍几种提高系统可靠性.可用性的方法. 扩展 扩展是最常见的提升系统可靠性的方法,系统的扩展可以避免单点故障,即 ...

  2. 调试 高通_高通平台调整通话音量

    文档说明 本文档以SC826-CN-01(msm8953平台,Android 7.1)为例,说明如何调节通话音量大小. 问题现象 客户板子通过耳机通道 HPH_L / HPH_R ->外部PA( ...

  3. java webservice 高并发_浅谈WEB中的高并发

    今天主要是对JAVA_WEB中高并发的概念及常见的处理手段做个基本介绍,后面会每个点都做详细的介绍及实现. 何谓高并发 高并发指的是:在同时或极短时间内,有大量的请求到达服务端,每个请求都需要服务端耗 ...

  4. ci mysql高并发_高并发访问mysql时的问题(一):库存超减

    标签: 如果在对某行记录的更新时不采取任何防范措施,在多线程访问时,就容易出现库存为负数的错误. 以下用php.mysql,apache ab工具举例说明: mysql表结构 CREATE TABLE ...

  5. 队列处理高并发_高并发场景下缓存处理的一些思路

    在实际的开发当中,我们经常需要进行磁盘数据的读取和搜索,因此经常会有出现从数据库读取数据的场景出现. 但是当数据访问量次数增大的时候,过多的磁盘读取可能会最终成为整个系统的性能瓶颈,甚至是压垮整个数据 ...

  6. greenplum配置高可用_高可用hadoop集群配置就收藏这一篇,动手搭建Hadoop(5)

    01 ssh免密安装 02 jdk安装 03 hadoop伪分布式安装 04 hadoop全分布式 完成了前面四步,现在做hadoop的高可用.其实和之前的lvs的高可用差不多的.如果我们有两个nam ...

  7. .net core高并发_高并发下的Node.js与负载均衡

    阅读本文约需要6分钟 大家好,我是你们的导师,我每天都会在这里给大家分享一些干货内容(当然了,周末也要允许老师休息一下哈).上次老师跟大家分享了下浅谈前端自动化构建的相关知识,今天跟大家分享浅谈前端自 ...

  8. 队列处理高并发_高并发架构消息队列面试题解析

    面试题 为什么使用消息队列? 消息队列有什么优点和缺点? Kafka.ActiveMQ.RabbitMQ.RocketMQ 都有什么区别,以及适合哪些场景? 面试官心理分析 其实面试官主要是想看看: ...

  9. keepalive+nginx实现负载均衡高可用_高可用、负载均衡 集群部署方案:Keepalived + Nginx + Tomcat...

    前言:初期应用较小,一般以单机部署为主,即可满足业务的需求,随着业务的不断扩大,单机部署的模式无法承载这么大的业务量,需要进行服务集群化的部署,本文主要介绍服务器Tomcat多实例部署,搭载Keepa ...

最新文章

  1. 最短路径问题 java实现 源代码
  2. c++ 关于char *的类库函数
  3. iPad的无纸化办公和纯纸化办公的区别和使用场景
  4. HDOJ 2049 不容易系列之(4)——考新郎
  5. Linux ps指令
  6. cocos2d-x游戏实例(14)-纵版射击游戏(1)
  7. Java最佳实践–队列之战和链接的ConcurrentHashMap
  8. 2020年8个效率最高的爬虫框架
  9. 腾讯开源业界首个云原生标准的一站式微服务管理框架Femas
  10. JavaScript笔记2———js的数据类型
  11. 控制台模块python_04_python常用模块
  12. Atitit 存储方法大总结 目录 1. 存储方式分类 2 1.1. 按照数据分类为 结构化 半结构化 非结构化 2 1.2. 按照内外部可分类 内部存储和外部存储持久化 2 1.3. 按照本地远
  13. Nagios社区真有意思
  14. 计算机应用数学,计算机应用数学.PDF
  15. Timer的源码分析
  16. 二进制 计算机编程语言分类
  17. 计算机网络中atm意思,atm是什么(atm什么意思)
  18. 苹果MAC电脑双系统教程——MAC安装Windows双系统教程
  19. 西门子三开接线图解_西门子三位单控开关怎么接线要开关实际图
  20. 基于机器学习场景,如何搭建特征数据管理中台?

热门文章

  1. 养生程序员之生活小窍门
  2. 浙江省计算机二级打印准考证时间,浙江计算机二级考试准考证打印时间
  3. CANON 面试有感
  4. [置顶] iphone 软解那点事 (二)
  5. python电文加密_电文加密
  6. 【计算机图形学基础】第一章绪论
  7. 免费开源Epub阅读器Koodo Reader Mac
  8. 51单片机8位抢答器程序设计与原理图搭建
  9. 5G网优工程师做了5年,我总结出了这3条避坑手册
  10. ubuntu18.04 安装CUDA9.0 + CUDNN7 deb安装