/*
* 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,
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
tinymix调试音频i2stinymix调试音频i2s
根据打印,先执行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