tinymix调试音频i2s

音频调试工具:audacity ,cool edit, gold wav Sonic Visualiser

http://www.360doc.com/content/18/0314/12/32862269_736896674.shtml
./hardware/qcom/audio/configs/msm8909/mixer_paths_msm8909_pm8916.xml中查看 ,这个配置文件是根据平台声卡名得来的

hardware/qcom/audio/hal/msm8916/platform.c

msm8909:/dev/snd # cat /proc/asound/cards                                      
 0 [msm8909pm8916sn]: msm8909-pm8916- - msm8909-pm8916-snd-card
                      msm8909-pm8916-snd-card
    } else if (!strncmp(snd_card_name, "msm8909-pm8916-snd-card",
                 sizeof("msm8909-pm8916-snd-card"))) {
        strlcpy(mixer_xml_path, MIXER_XML_PATH_MSM8909_PM8916,
                sizeof(MIXER_XML_PATH_MSM8909_PM8916));

        msm_device_to_be_id = msm_device_to_be_id_internal_codec;
        msm_be_id_array_len  =
            sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]);

    } 
msm8909:/dev/snd # cat /proc/asound/cards                                      
 0 [msm8909pm8916sn]: msm8909-pm8916- - msm8909-pm8916-snd-card
                      msm8909-pm8916-snd-card

xml包含codec切换path的配置,如 下面配有切换至speaker所需要的参数设置(通过tinymix进行配置)

驱动代码放在如下位置, 里面包含audio path切换的"audio_map"表格,

tinymix调试音频i2s_第1张图片
本平台为msm8909

注意,mixer_paths_msm8909_pm8916.xml中如下,只需要一个空格,否则无效
在这里插入图片描述
//headset








tinymix “PRI_MI2S_RX Audio Mixer MultiMedia1” “1”
tinymix “RX1 MIX1 INP1” “RX1”
tinymix “RDAC2 MUX” “RX1”
tinymix “RX1 Digital Volume” “84”
tinymix “EAR PA Gain” “POS_6_DB”
tinymix “EAR_S” “Switch”
tinyplay xxx.wav

tinymix ‘MI2S_RX Format’ ‘1’ 设置成24bit,i2s传输,设备也需要切到对应的i2s格式

//headphones









tinymix “PRI_MI2S_RX Audio Mixer MultiMedia1” “1”

SND_SOC_DAPM_AIF_OUT("PRI_MI2S_RX", "Primary MI2S Playback",
			     0, 0, 0, 0),snd_soc_dapm_aif_out    
   
实际为snd_soc_dapm_aif_out对应一个数字音频输出接口,比如I2S接口的输出端。如一组i2s的输出端

msm8909:/ # tinymix |grep "PRI_MI2S_RX Audio Mixer MultiMedia1"                
1460    BOOL    1       PRI_MI2S_RX Audio Mixer MultiMedia1      Off
1468    BOOL    1       PRI_MI2S_RX Audio Mixer MultiMedia10     Off
1469    BOOL    1       PRI_MI2S_RX Audio Mixer MultiMedia11     Off
1470    BOOL    1       PRI_MI2S_RX Audio Mixer MultiMedia12     Off
1471    BOOL    1       PRI_MI2S_RX Audio Mixer MultiMedia13     Off
1472    BOOL    1       PRI_MI2S_RX Audio Mixer MultiMedia14     Off
1473    BOOL    1       PRI_MI2S_RX Audio Mixer MultiMedia15     Off
1474    BOOL    1       PRI_MI2S_RX Audio Mixer MultiMedia16     Off

tinymix “MI2S_RX Channels” “Two”
tinymix “RX1 MIX1 INP1” “RX1”
tinymix “RX2 MIX1 INP1” “RX2”
tinymix “RDAC2 MUX” “RX2”
tinymix “HPHL” “Switch”
tinymix “HPHR” “Switch”

tinymix “RX2 Digital Volume” “84”

//mic adc1 如上面mic为adc1那路,那只找 adc1即可





tinymix “MultiMedia1 Mixer TERT_MI2S_TX” 1
tinymix “ADC1 Volume” 6
tinymix “DEC1 MUX” “ADC1”
tinycap /data/mic1.wav

//speaker





tinymix ‘PRI_MI2S_RX Audio Mixer MultiMedia1’ 1
tinymix ‘RX3 MIX1 INP1’ ‘RX1’
tinymix “RX1 Digital Volume” “84”
tinymix “RX1 Digital Volume” “60”
tinymix “SPK” “Switch”
tinyplay /data/01_1KHz_0dB.wav

以上的控制功能都是由kcontrol提供的如pm8916上的kcontrol

static const struct snd_kcontrol_new msm8x16_wcd_snd_controls[] = {

	SOC_ENUM_EXT("RX HPH Mode", msm8x16_wcd_hph_mode_ctl_enum[0],
		msm8x16_wcd_hph_mode_get, msm8x16_wcd_hph_mode_set),

	SOC_ENUM_EXT("Boost Option", msm8x16_wcd_boost_option_ctl_enum[0],
		msm8x16_wcd_boost_option_get, msm8x16_wcd_boost_option_set),

	SOC_ENUM_EXT("EAR PA Boost", msm8x16_wcd_ear_pa_boost_ctl_enum[0],
		msm8x16_wcd_ear_pa_boost_get, msm8x16_wcd_ear_pa_boost_set),

	SOC_ENUM_EXT("EAR PA Gain", msm8x16_wcd_ear_pa_gain_enum[0],
		msm8x16_wcd_pa_gain_get, msm8x16_wcd_pa_gain_put),

	SOC_ENUM_EXT("Speaker Boost", msm8x16_wcd_spk_boost_ctl_enum[0],
		msm8x16_wcd_spk_boost_get, msm8x16_wcd_spk_boost_set),

	SOC_ENUM_EXT("Ext Spk Boost", msm8x16_wcd_ext_spk_boost_ctl_enum[0],
		msm8x16_wcd_ext_spk_boost_get, msm8x16_wcd_ext_spk_boost_set),

	SOC_ENUM_EXT("LOOPBACK Mode", msm8x16_wcd_loopback_mode_ctl_enum[0],
		msm8x16_wcd_loopback_mode_get, msm8x16_wcd_loopback_mode_put),

	SOC_SINGLE_TLV("ADC1 Volume", MSM8X16_WCD_A_ANALOG_TX_1_EN, 3,
					8, 0, analog_gain),
	SOC_SINGLE_TLV("ADC2 Volume", MSM8X16_WCD_A_ANALOG_TX_2_EN, 3,
					8, 0, analog_gain),
	SOC_SINGLE_TLV("ADC3 Volume", MSM8X16_WCD_A_ANALOG_TX_3_EN, 3,
					8, 0, analog_gain),
SOC_ENUM_EXT("EAR PA Gain", msm8x16_wcd_ear_pa_gain_enum[0],
		msm8x16_wcd_pa_gain_get, msm8x16_wcd_pa_gain_put),

如ear的增益
"EAR PA Gain"是暴露给tinymix的属性名字
msm8x16_wcd_ear_pa_gain_enum[0]是对应可供选择的值
“POS_1P5_DB”, “POS_6_DB”
msm8x16_wcd_pa_gain_get, msm8x16_wcd_pa_gain_put是对应的操作函数


static const char * const msm8x16_wcd_ear_pa_gain_text[] = {
		"POS_1P5_DB", "POS_6_DB"};
static const struct soc_enum msm8x16_wcd_ear_pa_gain_enum[] = {
		SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_ear_pa_gain_text),
};

对应的操作接口set

static int msm8x16_wcd_pa_gain_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	u8 ear_pa_gain;
	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);

	dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
		__func__, ucontrol->value.integer.value[0]);

	switch (ucontrol->value.integer.value[0]) {
	case 0: //对应的枚举值
		ear_pa_gain = 0x00;
		break;
	case 1:
		ear_pa_gain = 0x20;
		break;
	default:
		return -EINVAL;
	}

	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_RX_EAR_CTL,
			    0x20, ear_pa_gain);
	return 0;
}

ls -al /dev/snd/下面的文件
音频设备的命名规则为 [device type]C[card index]D[device index][capture/playback],即名字中含有4部分的信息:
device type
设备类型,通常只有compr/hw/pcm这3种。从上图可以看到声卡会管理很多设备,PCM设备只是其中的一种设备。
card index
声卡的id,代表第几块声卡。通常都是0,代表第一块声卡。手机上通常都只有一块声卡。
device index
设备的id,代表这个设备是声卡上的第几个设备。设备的ID只和驱动中配置的DAI link的次序有关。如果驱动没有改变,那么这些ID就是固定的。

msm8909:/dev/snd # cat /sys/kernel/debug/asoc/dais                             
cajon_vifeedback
msm8x16_wcd_i2s_tx1
msm8x16_wcd_i2s_rx1
tas5751-i2s
MultiMedia29
MultiMedia28
MultiMedia21
MultiMedia20
MultiMedia19
MultiMedia18
MultiMedia17
VoiceMMode2
VoiceMMode1
MultiMedia16
MultiMedia15
MultiMedia14
MultiMedia13
MultiMedia12
MultiMedia11
MultiMedia10
VoWLAN
LSM8
LSM7
LSM6
LSM5
LSM4
LSM3
LSM2
LSM1

前面已经创建了control设备,现在soc_probe_link_dais调用soc_new_pcm创建pcm设备。
1)设置pcm native中要使用的pcm操作函数,这些函数用于操作音频物理设备,包括machine、codec_dai、cpu_dai、platform;
2)调用snd_pcm_new()创建pcm设备,回放子流实例和录制子流实例都在这里创建;
3)回调platform驱动的pcm_new(),完成音频dma设备初始化和dma buffer内存分配;

soc-core.c
soc_probe
platform_get_drvdata(pdev)//获取声卡
snd_soc_register_card
snd_soc_instantiate_card
soc_probe_link_dais
soc-pcm.c
soc_new_pcm

capture/playback
只有PCM设备才有这部分,只有c和p两种。c代表capture,说明这是一个提供录音的设备,p代表palyback,说明这是一个提供播放的设备。
系统会在/proc/asound/pcm文件中列出所有的音频设备的信息,如果是肉眼查看,/proc/asound/pcm中的信息会更直观一些

调用流程:
tinymix调试音频i2s_第2张图片
tinyplay调用流程
从pcm的操作函数开始

const struct file_operations snd_pcm_f_ops[2] = {                                                                                              
        {                                                                                                                                      
                .owner =                THIS_MODULE,                                                                                           
                .write =                snd_pcm_write,                                                                                         
                .aio_write =            snd_pcm_aio_write,                                                                                     
                .open =                 snd_pcm_playback_open,                                                                                 
                .release =              snd_pcm_release,                                                                                       
                .llseek =               no_llseek,                                                                                             
                .poll =                 snd_pcm_playback_poll,                                                                                 
                .unlocked_ioctl =       snd_pcm_playback_ioctl,                                                                                
                .compat_ioctl =         snd_pcm_ioctl_compat,                                                                                  
                .mmap =                 snd_pcm_mmap,                                                                                          
                .fasync =               snd_pcm_fasync,                                                                                        
                .get_unmapped_area =    snd_pcm_get_unmapped_area,                                                                             
        },                                                                                                                                     
        {                                                                                                                                      
                .owner =                THIS_MODULE,                                                                                           
                .read =                 snd_pcm_read,                                                                                          
                .aio_read =             snd_pcm_aio_read,                                                                                      
                .open =                 snd_pcm_capture_open,                                                                                  
                .release =              snd_pcm_release,                                                                                       
                .llseek =               no_llseek,                                                                                             
                .poll =                 snd_pcm_capture_poll,                                                                                  
                .unlocked_ioctl =       snd_pcm_capture_ioctl,                                                                                 
                .compat_ioctl =         snd_pcm_ioctl_compat,                                                                                  
                .mmap =                 snd_pcm_mmap,                                                                                          
                .fasync =               snd_pcm_fasync,                                                                                        
                .get_unmapped_area =    snd_pcm_get_unmapped_area,                                                                             
        }                                                                                                                                      
}; 

snd_pcm_playback_open
snd_pcm_open
snd_pcm_open_file
snd_pcm_open_substream-》substream->ops->open在

soc_new_pcm中注册回调的
        /* ASoC PCM operations */
        if (rtd->dai_link->dynamic) {
                rtd->ops.open           = dpcm_fe_dai_open;
                rtd->ops.hw_params      = dpcm_fe_dai_hw_params;
                rtd->ops.prepare        = dpcm_fe_dai_prepare;
                rtd->ops.trigger        = dpcm_fe_dai_trigger;
                rtd->ops.hw_free        = dpcm_fe_dai_hw_free;
                rtd->ops.close          = dpcm_fe_dai_close;
                rtd->ops.pointer        = soc_pcm_pointer;
                rtd->ops.delay_blk      = soc_pcm_delay_blk;
                rtd->ops.ioctl          = soc_pcm_ioctl;
                rtd->ops.compat_ioctl   = soc_pcm_compat_ioctl;
        } else {
                rtd->ops.open           = soc_pcm_open;
                rtd->ops.hw_params      = soc_pcm_hw_params;
                rtd->ops.prepare        = soc_pcm_prepare;
                rtd->ops.trigger        = soc_pcm_trigger;
                rtd->ops.hw_free        = soc_pcm_hw_free;
                rtd->ops.close          = soc_pcm_close;
                rtd->ops.pointer        = soc_pcm_pointer;
                rtd->ops.delay_blk      = soc_pcm_delay_blk;
                rtd->ops.ioctl          = soc_pcm_ioctl;
                rtd->ops.compat_ioctl   = soc_pcm_compat_ioctl;
        }    

        if (platform->driver->ops) {
                rtd->ops.ack            = platform->driver->ops->ack;
                rtd->ops.copy           = platform->driver->ops->copy;
                rtd->ops.silence        = platform->driver->ops->silence;
                rtd->ops.page           = platform->driver->ops->page;
                rtd->ops.mmap           = platform->driver->ops->mmap;
                rtd->ops.restart        = platform->driver->ops->restart;
        }    
				
			soc_pcm_open
/*
 * Called by ALSA when a PCM substream is opened, the runtime->hw record is
 * then initialized and any private data can be allocated. This also calls
 * startup for the cpu DAI, platform, machine and codec DAI.
 */
static int soc_pcm_open(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_soc_platform *platform = rtd->platform;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct snd_soc_dai *codec_dai;
	const char *codec_dai_name = "multicodec";
	int i, ret = 0;

	pinctrl_pm_select_default_state(cpu_dai->dev);
	for (i = 0; i < rtd->num_codecs; i++)
		pinctrl_pm_select_default_state(rtd->codec_dais[i]->dev);
	pm_runtime_get_sync(cpu_dai->dev);
	for (i = 0; i < rtd->num_codecs; i++)
		pm_runtime_get_sync(rtd->codec_dais[i]->dev);
	pm_runtime_get_sync(platform->dev);

	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
	if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST)
		snd_soc_set_runtime_hwparams(substream, &no_host_hardware);

	/* startup the audio subsystem */操作cpu dai的startup接口
	if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) {
		ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
		if (ret < 0) {
			dev_err(cpu_dai->dev, "ASoC: can't open interface"
				" %s: %d\n", cpu_dai->name, ret);
			goto out;
		}
	}
操作platform的open接口
	if (platform->driver->ops && platform->driver->ops->open) {
		ret = platform->driver->ops->open(substream);
		if (ret < 0) {
			dev_err(platform->dev, "ASoC: can't open platform"
				" %s: %d\n", platform->component.name, ret);
			goto platform_err;
		}
	}
操作codec dai的startup接口
	for (i = 0; i < rtd->num_codecs; i++) {
		codec_dai = rtd->codec_dais[i];
		if (codec_dai->driver->ops && codec_dai->driver->ops->startup) {
			ret = codec_dai->driver->ops->startup(substream,
							      codec_dai);
			if (ret < 0) {
				dev_err(codec_dai->dev,
					"ASoC: can't open codec %s: %d\n",
					codec_dai->name, ret);
				goto codec_dai_err;
			}
		}

		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
			codec_dai->tx_mask = 0;
		else
			codec_dai->rx_mask = 0;
	}
操作 dai  link的startup接口
	if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
		ret = rtd->dai_link->ops->startup(substream);
		if (ret < 0) {
			pr_err("ASoC: %s startup failed: %d\n",
			       rtd->dai_link->name, ret);
			goto machine_err;
		}
	}

	/* Dynamic PCM DAI links compat checks use dynamic capabilities */
	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
		goto dynamic;

	/* Check that the codec and cpu DAIs are compatible */
	soc_pcm_init_runtime_hw(substream);

	if (rtd->num_codecs == 1)
		codec_dai_name = rtd->codec_dai->name;

	if (soc_pcm_has_symmetry(substream))
		runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;

	ret = -EINVAL;
	if (!runtime->hw.rates) {
		printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
			codec_dai_name, cpu_dai->name);
		goto config_err;
	}
	if (!runtime->hw.formats) {
		printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n",
			codec_dai_name, cpu_dai->name);
		goto config_err;
	}
	if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
	    runtime->hw.channels_min > runtime->hw.channels_max) {
		printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n",
				codec_dai_name, cpu_dai->name);
		goto config_err;
	}

	soc_pcm_apply_msb(substream);

	/* Symmetry only applies if we've already got an active stream. */
	if (cpu_dai->active) {
		ret = soc_pcm_apply_symmetry(substream, cpu_dai);
		if (ret != 0)
			goto config_err;
	}

	for (i = 0; i < rtd->num_codecs; i++) {
		if (rtd->codec_dais[i]->active) {
			ret = soc_pcm_apply_symmetry(substream,
						     rtd->codec_dais[i]);
			if (ret != 0)
				goto config_err;
		}
	}

	pr_debug("ASoC: %s <-> %s info:\n",
			codec_dai_name, cpu_dai->name);
	pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates);
	pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min,
		 runtime->hw.channels_max);
	pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min,
		 runtime->hw.rate_max);

dynamic:

	snd_soc_runtime_activate(rtd, substream->stream);

	mutex_unlock(&rtd->pcm_mutex);
	return 0;

config_err://出错处理,shutdown dai link
	if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
		rtd->dai_link->ops->shutdown(substream);

machine_err:
	i = rtd->num_codecs;

codec_dai_err:
	while (--i >= 0) {//出错处理,shutdown codec dai 
		codec_dai = rtd->codec_dais[i];
		if (codec_dai->driver->ops->shutdown)
			codec_dai->driver->ops->shutdown(substream, codec_dai);
	}
//出错处理,close platform
	if (platform->driver->ops && platform->driver->ops->close)
		platform->driver->ops->close(substream);

platform_err://出错处理,shutdown cpu dai 
	if (cpu_dai->driver->ops && cpu_dai->driver->ops->shutdown)
		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
out:
	mutex_unlock(&rtd->pcm_mutex);

	pm_runtime_put(platform->dev);
	for (i = 0; i < rtd->num_codecs; i++)
		pm_runtime_put(rtd->codec_dais[i]->dev);
	pm_runtime_put(cpu_dai->dev);
	for (i = 0; i < rtd->num_codecs; i++) {
		if (!rtd->codec_dais[i]->active)
			pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
	}
	if (!cpu_dai->active)
		pinctrl_pm_select_sleep_state(cpu_dai->dev);

	return ret;
}

		.name = LPASS_BE_QUAT_MI2S_TX,
		.stream_name = "Quaternary MI2S Capture",
		.cpu_dai_name = "msm-dai-q6-mi2s.3",
		.platform_name = "msm-pcm-routing",
		.codec_dai_name = "snd-soc-dummy-dai",
		.codec_name = "snd-soc-dummy",
		.no_pcm = 1,
		.dpcm_capture = 1,
		.be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
		.be_hw_params_fixup = msm_be_hw_params_fixup,
		.ops = &msm8952_quat_mi2s_be_ops,
		.ignore_suspend = 1,


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

{
		.playback = {
			.stream_name = "Quaternary MI2S Playback",
			.aif_name = "QUAT_MI2S_RX",
			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
			SNDRV_PCM_RATE_192000,
			.formats = SNDRV_PCM_FMTBIT_S16_LE,
			.rate_min =     8000,
			.rate_max =     192000,
		},
		.capture = {
			.stream_name = "Quaternary MI2S Capture",
			.aif_name = "QUAT_MI2S_TX",
			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
			SNDRV_PCM_RATE_16000,
			.formats = SNDRV_PCM_FMTBIT_S16_LE,
			.rate_min =     8000,
			.rate_max =     48000,
		},
		.ops = &msm_dai_q6_mi2s_ops,
		.id = MSM_QUAT_MI2S,
		.probe = msm_dai_q6_dai_mi2s_probe,
		.remove = msm_dai_q6_dai_mi2s_remove,
	}
	static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = {
	.startup	= msm_dai_q6_mi2s_startup,
	.prepare	= msm_dai_q6_mi2s_prepare,
	.hw_params	= msm_dai_q6_mi2s_hw_params,
	.hw_free	= msm_dai_q6_mi2s_hw_free,
	.set_fmt	= msm_dai_q6_mi2s_set_fmt,
	.shutdown	= msm_dai_q6_mi2s_shutdown,
};


msm-pcm-routing
static struct snd_soc_platform_driver msm_soc_routing_platform = {
	.ops		= &msm_routing_pcm_ops,
	.probe		= msm_routing_probe,
	.pcm_new	= msm_routing_pcm_new,
	.pcm_free	= msm_routing_pcm_free,
};
static struct snd_pcm_ops msm_routing_pcm_ops = {
	.hw_params	= msm_pcm_routing_hw_params,
	.close          = msm_pcm_routing_close,
	.prepare        = msm_pcm_routing_prepare,
};


snd-soc-dummy-dai
static struct snd_soc_dai_driver dummy_dai = {
	.name = "snd-soc-dummy-dai",
	.playback = {
		.stream_name	= "Playback",
		.channels_min	= 1,
		.channels_max	= 384,
		.rates		= STUB_RATES,
		.formats	= STUB_FORMATS,
	},
	.capture = {
		.stream_name	= "Capture",
		.channels_min	= 1,
		.channels_max	= 384,
		.rates = STUB_RATES,
		.formats = STUB_FORMATS,
	 },
};
static struct snd_pcm_ops dummy_dma_ops = {
	.open		= dummy_dma_open,
	.ioctl		= snd_pcm_lib_ioctl,
};

static struct snd_soc_platform_driver dummy_platform = {
	.ops = &dummy_dma_ops,
};
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
	const struct snd_pcm_hardware *hw)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	if (!runtime)
		return 0;
	runtime->hw.info = hw->info;
	runtime->hw.formats = hw->formats;
	runtime->hw.period_bytes_min = hw->period_bytes_min;
	runtime->hw.period_bytes_max = hw->period_bytes_max;
	runtime->hw.periods_min = hw->periods_min;
	runtime->hw.periods_max = hw->periods_max;
	runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
	runtime->hw.fifo_size = hw->fifo_size;
	return 0;
}


dai_link:

        {    
                .name = LPASS_BE_QUAT_MI2S_TX,
                .stream_name = "Quaternary MI2S Capture",
                .cpu_dai_name = "msm-dai-q6-mi2s.3",
                .platform_name = "msm-pcm-routing",
                .codec_dai_name = "snd-soc-dummy-dai",
                .codec_name = "snd-soc-dummy",
                .no_pcm = 1, 
                .dpcm_capture = 1, 
                .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
                .be_hw_params_fixup = msm_be_hw_params_fixup,
                .ops = &msm8952_quat_mi2s_be_ops,
                .ignore_suspend = 1, 

static struct snd_soc_ops msm8952_quat_mi2s_be_ops = {
        .startup = msm_quat_mi2s_snd_startup,
        .hw_params = msm_mi2s_snd_hw_params,
        .shutdown = msm_quat_mi2s_snd_shutdown,
};


static int msm_quat_mi2s_snd_startup(struct snd_pcm_substream *substream)
{
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_card *card = rtd->card;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct msm8916_asoc_mach_data *pdata =
                        snd_soc_card_get_drvdata(card);
        int ret = 0, val = 0;

        pr_debug("%s(): substream = %s  stream = %d\n", __func__,
                                substream->name, substream->stream);

        if (!q6core_is_adsp_ready()) {
                pr_err("%s(): adsp not ready\n", __func__);
                return -EINVAL;
        }

        if (pdata->vaddr_gpio_mux_mic_ctl) {
                val = ioread32(pdata->vaddr_gpio_mux_mic_ctl);
                val = val | 0x02020002;
                iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl);
        }
        ret = msm_mi2s_sclk_ctl(substream, true);
        if (ret < 0) {
                pr_err("failed to enable sclk\n");
                return ret;
        }
        ret = msm_gpioset_activate(CLIENT_WCD_INT, "quat_i2s");
        if (ret < 0) {
                pr_err("failed to enable codec gpios\n");
                goto err;
        }
        if (atomic_inc_return(&quat_mi2s_clk_ref) == 1) {
                ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
                if (ret < 0)
                        pr_err("%s: set fmt cpu dai failed\n", __func__);
        }
        return ret;
err:
        ret = msm_mi2s_sclk_ctl(substream, false);
        if (ret < 0)
                pr_err("failed to disable sclk\n");
        return ret;
}

tinymix ‘QUAT_MI2S_RX Audio Mixer MultiMedia1’ 1
tinyplay /data/1.wav
QUAT_MI2S_RX Audio Mixer是一个widget ,这里其实和MultiMedia1就决定了后端的端口号,即将要打开的port_id
MultiMedia1是一个snd_kcontrol_new,根据情况分析,这里为对应的前端,一般MultiMedia1对应alsa前端即上层打开的设备号,对应的声卡设备如下,MultiMedia1 为c0d0
tinyplay tinymix里面程序默认打开是c0d0p因此能播放,若是MultiMedia5刚不是c0d0p,刚tinymix tinyplay要指定c号d号是多少,详细参见tinyplay命令

程序中用pcm_open打开需要带device id号,如muutimedia6 card是0,device是18
00-18: MultiMedia6 (*) : : playback 1 : capture 1
struct pcm *pcm_open(unsigned int card, unsigned int device, unsigned int flags, struct pcm_config *config);
则device要为18

这里的c号d号为codec在snd_soc_dai_link的数组下标,MultiMedia6为结构中数组中的cpu_dai_name名字
tinymix调试音频i2s_第3张图片

msm8909:/proc/asound # cat pcm 
cat pcm 
00-00: MultiMedia1 (*) : : playback 1 : capture 1 
00-01: MultiMedia2 (*) : : playback 1 : capture 1 
00-02: CS-Voice (*) : : playback 1 : capture 1 
00-03: VoIP (*) : : playback 1 : capture 1 
00-04: ULL (*) : : playback 1 
00-05: Primary MI2S_RX Hostless (*) : : playback 1 
00-06: INT_FM Hostless (*) : : capture 1 
00-07: AFE-PROXY RX msm-stub-rx-7 : : playback 1 
00-08: AFE-PROXY TX msm-stub-tx-8 : : capture 1 
00-09: (Compress1) : : playback 1 : capture 1 
00-10: AUXPCM Hostless (*) : : playback 1 : capture 1 
00-11: Tertiary MI2S_TX Hostless (*) : : capture 1 
00-12: MultiMedia5 (*) : : playback 1 : capture 1 
00-13: Voice2 (*) : : playback 1 : capture 1 
00-14: MultiMedia9 (*) : : playback 1 : capture 1 
00-15: VoLTE (*) : : playback 1 : capture 1 
00-16: VoWLAN (*) : : playback 1 : capture 1 
00-17: INT_HFP_BT Hostless (*) : : playback 1 : capture 1 
00-18: MultiMedia6 (*) : : playback 1 : capture 1 
00-19: Listen 1 Audio Service (*) : : capture 1 
00-20: Listen 2 Audio Service (*) : : capture 1 
00-21: Listen 3 Audio Service (*) : : capture 1 
00-22: Listen 4 Audio Service (*) : : capture 1 
00-23: Listen 5 Audio Service (*) : : capture 1 
00-24: (Compress2) : : playback 1 
00-25: QUAT_MI2S Hostless (*) : : playback 1 
00-26: Senary_mi2s Capture cajon_vifeedback-26 : : capture 1 
00-27: (Compress3) : : playback 1 
00-28: (Compress4) : : playback 1 
00-29: (Compress5) : : playback 1 
00-30: (Compress6) : : playback 1 
00-31: (Compress7) : : playback 1 
00-32: (Compress8) : : playback 1 
00-33: (Compress9) : : playback 1 
00-34: VoiceMMode1 (*) : : playback 1 : capture 1 
00-35: VoiceMMode2 (*) : : playback 1 : capture 1 
00-36: MultiMedia8 (*) : : playback 1 : capture 1 
00-37: QCHAT (*) : : playback 1 : capture 1 
00-38: (Compress10) : : capture 1 
00-39: (Compress11) : : capture 1 
00-40: (Compress12) : : capture 1 
00-41: (Compress13) : : capture 1 
00-42: (Compress14) : : capture 1 
00-43: (Primary MI2S Playback) : : playback 1 
00-44: (Secondary MI2S Playback) : : playback 1 
00-45: (Tertiary MI2S Capture) : : capture 1 
00-46: (Quaternary MI2S Playback) : : playback 1 
00-47: (Quaternary MI2S Capture) : : capture 1 
00-48: (AUX PCM Playback) : : playback 1 
00-49: (AUX PCM Capture) : : capture 1 
00-50: (Internal BT-SCO Playback) : : playback 1 
00-51: (Internal BT-SCO Capture) : : capture 1 
00-52: (Internal FM Playback) : : playback 1 
00-53: (Internal FM Capture) : : capture 1 
00-54: (AFE Playback) : : playback 1 
00-55: (AFE Capture) : : capture 1 
00-56: (Voice Uplink Capture) : : capture 1 
00-57: (Voice Downlink Capture) : : capture 1 
00-58: (Voice Farend Playback) : : playback 1 
00-59: (Voice2 Farend Playback) : : playback 1 
00-60: (Quinary MI2S Capture) : : capture 1 
00-61: (Quinary MI2S Playback) : : playback 1 
00-62: (Internal BT-A2DP Playback) : : playback 1 
msm8909:/proc/asound # cat pcm|grep BT 
cat pcm|grep BT 
00-17: INT_HFP_BT Hostless (*) : : playback 1 : capture 1 
00-50: (Internal BT-SCO Playback) : : playback 1 
00-51: (Internal BT-SCO Capture) : : capture 1 
00-62: (Internal BT-A2DP Playback) : : playback 1
---------------------

tinymix调试音频i2s_第4张图片
tinymix调试音频i2s_第5张图片

#define SND_SOC_DAPM_SIGGEN(wname) \  
{       .id = snd_soc_dapm_siggen, .name = wname, .kcontrol_news = NULL, \  
        .num_kcontrols = 0, .reg = SND_SOC_NOPM }  
#define SND_SOC_DAPM_INPUT(wname) \  
{       .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \  
        .num_kcontrols = 0, .reg = SND_SOC_NOPM }  
#define SND_SOC_DAPM_OUTPUT(wname) \  
{       .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \  
        .num_kcontrols = 0, .reg = SND_SOC_NOPM }  
#define SND_SOC_DAPM_MIC(wname, wevent) \  
{       .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \  
        .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \  
        .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}  
#define SND_SOC_DAPM_HP(wname, wevent) \  
{       .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \  
        .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \  
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}  
#define SND_SOC_DAPM_SPK(wname, wevent) \  
{       .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \  
        .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \  
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}  
#define SND_SOC_DAPM_LINE(wname, wevent) \  
{       .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \  
        .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \  
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}  

以上这些widget分别对应信号发生器,输入引脚,输出引脚,麦克风,耳机,扬声器,线路输入接口。其中的reg字段被设置为SND_SOC_NOPM(-1),表明这些widget是没有寄存器控制位来控制widget的电源状态的。麦克风,耳机,扬声器,线路输入接口这几种widget,还可以定义一个dapm事件回调函数wevent,从event_flags字段的设置可以看出,他们只会响应SND_SOC_DAPM_POST_PMU(上电后)和SND_SOC_DAPM_PMD(下电前)事件,这几个widget通常会在machine驱动中定义,而SND_SOC_DAPM_INPUT和SND_SOC_DAPM_OUTPUT则用来定义codec芯片的输出输入脚,通常在codec驱动中定义,最后,在machine驱动中增加相应的route,把麦克风和耳机等widget与相应的codec输入输出引脚的widget连接起来
tinymix调试音频i2s_第6张图片

根据打印,先执行get,获取到值,1,再执行put,将值用于设置连接
将snd_soc_dapm_widget与snd_kcontrol_new连接

inymix 'QUAT_MI2S_RX Audio Mixer MultiMedia1' 1                               <
[  340.617577] msm_routing_get_audio_mixer: reg 1e shift 0 val 0
[  340.622612] msm_pcm_routing_process_audio: reg 1e val 0 set 1

tinymix调试音频i2s_第7张图片
tinymix调试音频i2s_第8张图片

msm_pcm_routing_process_audio

snd_soc_dapm_mixer_update_power
soc_dpcm_runtime_update
dpcm_process_paths[[[[[[— Create any new FE <–> BE connections–dpcm_be_connect-> list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);]]]]]]

			dpcm_run_new_update
				dpcm_run_update_startup
					dpcm_be_dai_startup
							soc_pcm_open
struct snd_soc_dapm_route {  
        const char *sink;  
        const char *control;  
        const char *source;  
        int (*connected)(struct snd_soc_dapm_widget *source,  
                         struct snd_soc_dapm_widget *sink);  
};  

注意该结构体的注释, sink 是目的部件, source 是源部件, control 是目的部件定义的
kcontrol ;通过 control 可以选择 source 作为 sink 的输入源
ink指向到达端widget的名字字符串,source指向起始端widget的名字字符串,control指向负责控制该连接所对应的kcontrol名字字符串,connected回调则定义了上一节所提到的自定义连接检查回调函数。该结构的意义很明显就是:source通过一个kcontrol,和sink连接在一起,现在是否处于连接状态,请调用connected回调函数检查。
tinymix调试音频i2s_第9张图片

struct snd_soc_dapm_path {
	const char *name;

	/* source (input) and sink (output) widgets */
	struct snd_soc_dapm_widget *source;
	struct snd_soc_dapm_widget *sink;

如上数据结构可以看出path是sink与source是两个widget
下面snd_soc_dapm_route中
{“QUAT_MI2S_RX Port Mixer”, “INTERNAL_BT_SCO_TX”,“INT_BT_SCO_TX”},
"QUAT_MI2S_RX Port Mixer"为widget的名字
"INT_BT_SCO_TX"为widget的名字,代表一个输入设备,数据流入此设备为in

SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
				0, 0, 0, 0),

"INTERNAL_BT_SCO_TX"为负责控制该连接所对应的kcontrol名字字符串

tinymix controler的函数流程:

[   48.684637] (2)[2357:AudioHfpThread]------------[ cut here ]------------
[   48.684644] (1)[233:logd.auditd]type=1400 audit(1262304054.036:226): avc: denied { getattr } for pid=243 comm="AudioHfpThread" path="/dev/__properties__/u:object_r:bluetooth_prop:s0" dev="tmpfs" ino=160 scontext=u:r:mtk_hal_audio:s0 tcontext=u:object_r:bluetooth_prop:s0 tclass=file permissive=1
[   48.684671] (2)[2357:AudioHfpThread]WARNING: CPU: 2 PID: 2357 at /code/ch007Go/kernel-3.18/sound/soc/mediatek/mt6580/mt_soc_codec_63xx.c:2490 Headset_PGAR_Set+0x1c/0x84()
[   48.684684] (2)[2357:AudioHfpThread]Modules linked in: wlan_drv bf0fe000   (null) 937604 0 (O) wmt_chrdev_wifi bf0f9000   (null) 5642 0 (O) gps_drv bf0ef000   (null) 21666 0 (O) bt_drv bf0e8000   (null) 11775 0 (O) wmt_drv bf000000   (null) 818500 0 (O)
[   48.684749] -(2)[2357:AudioHfpThread]CPU: 2 PID: 2357 Comm: AudioHfpThread Tainted: G        W  O   3.18.79 #3
[   48.684760] -(2)[2357:AudioHfpThread]Backtrace: 
[   48.684786] -(2)[2357:AudioHfpThread][<c010b7b4>] (dump_backtrace) from [<c010b9cc>] (show_stack+0x18/0x1c)
[   48.684797] -(2)[2357:AudioHfpThread] r7:00000000 r6:00000000 r5:600e0013 r4:c0d41a54
[   48.684837] -(2)[2357:AudioHfpThread][<c010b9b4>] (show_stack) from [<c08bbd70>] (dump_stack+0x88/0xa8)
[   48.684857] -(2)[2357:AudioHfpThread][<c08bbce8>] (dump_stack) from [<c0122ed8>] (warn_slowpath_common+0x70/0x94)
[   48.684867] -(2)[2357:AudioHfpThread] r7:000009ba r6:c0af3f19 r5:00000009 r4:00000000
[   48.684906] -(2)[2357:AudioHfpThread][<c0122e68>] (warn_slowpath_common) from [<c0122fe8>] (warn_slowpath_null+0x24/0x2c)
[   48.684917] -(2)[2357:AudioHfpThread] r8:de4f6400 r7:c2164000 r6:dd822238 r5:dd822200
[   48.684955] -(2)[2357:AudioHfpThread][<c0122fc4>] (warn_slowpath_null) from [<c0722e28>] (Headset_PGAR_Set+0x1c/0x84)
case SNDRV_CTL_IOCTL_ELEM_WRITE:
	return snd_ctl_elem_write_user(ctl, argp);->snd_ctl_elem_write-》result = kctl->put(kctl, control);
[   48.684973] -(2)[2357:AudioHfpThread][<c0722e0c>] (Headset_PGAR_Set) from [<c06bac60>] (snd_ctl_ioctl+0x2e4/0xc74)
[   48.684984] -(2)[2357:AudioHfpThread] r5:dd822200 r4:b028e4b0
[   48.685011] -(2)[2357:AudioHfpThread][<c06ba97c>] (snd_ctl_ioctl) from [<c023940c>] (do_vfs_ioctl+0x9c/0x69c)
[   48.685022] -(2)[2357:AudioHfpThread] r10:c0d1fe48 r9:00000008 r8:b028e4b0 r7:c2c85513
[   48.685058] -(2)[2357:AudioHfpThread][<c0239370>] (do_vfs_ioctl) from [<c0239a60>] (SyS_ioctl+0x54/0x78)
[   48.685069] -(2)[2357:AudioHfpThread] r10:00000000 r9:00000008 r8:b028e4b0 r7:c2c85513
[   48.685107] -(2)[2357:AudioHfpThread][<c0239a0c>] (SyS_ioctl) from [<c01064c0>] (ret_fast_syscall+0x0/0x44)
[   48.685121] (2)[2357:AudioHfpThread]---[ end trace 077b2ee3a8ba33d4 ]---

snd_soc_add_codec_controls-》snd_soc_add_controls-》snd_ctl_add

snd_ctl_add(card, snd_soc_cnew(control, data,
						     control->name, prefix))

snd_soc_cnew-》snd_ctl_new1是snd_kcontrol_new转换成snd_kcontrol的地方
如名字:snd_kcontrol kctl->id.name-》snd_kcontrol_new的name

snd_ctl_elem_write
加调试打印即可追踪:

--- a/kernel-3.18/sound/core/control.c
+++ b/kernel-3.18/sound/core/control.c
@@ -907,6 +907,7 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
                        result = -EPERM;
                } else {
                        snd_ctl_build_ioff(&control->id, kctl, index_offset);
+                       printk("%s %d name:%s value:%ld func:%pF zhongyukang \n",__FUNCTION__,__LINE__,kctl->id.name,control->value.integer.value[0],kctl->put);
                        result = kctl->put(kctl, control);
                }
                if (result > 0) {

你可能感兴趣的:(android)