Android音訊驅動-ASOC之PCM Prepare
阿新 • • 發佈:2019-01-05
ALSA的Prepare流程
snd_pcm_prepare => snd_pcm_action_nonatomic => snd_pcm_action_group => snd_pcm_do_prepare =>
substream->ops->prepare =>soc_pcm_prepare =>rtd->dai_link->ops->prepare => platform->driver->ops->prepare
=>codec_dai->driver->ops->prepare => cpu_dai->driver->ops->prepare
int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
{
struct snd_xferi x;
if (pcm->flags & PCM_IN)
return -EINVAL;
x.buf = (void*)data;
x.frames = count / (pcm->config.channels *
pcm_format_to_bits(pcm->config.format) / 8 );
for (;;) {
if (!pcm->running) {
int prepare_error = pcm_prepare(pcm);//write之前先prepare
if (prepare_error)
return prepare_error;
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
return oops(pcm, errno, "cannot write initial data" );
pcm->running = 1;
return 0;
}
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
pcm->prepared = 0;
pcm->running = 0;
if (errno == EPIPE) {
pcm->underruns++;
if (pcm->flags & PCM_NORESTART)
return -EPIPE;
continue;
}
return oops(pcm, errno, "cannot write stream data");
}
return 0;
}
}
// /external/tinyalsa/pcm.c,使用者層通過ioctl的方式來呼叫kernel
int pcm_prepare(struct pcm *pcm)
{
if (pcm->prepared)
return 0;
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
return oops(pcm, errno, "cannot prepare channel");
pcm->prepared = 1;
return 0;
}
// /kernel-3.18/sound/core/pcm_native.c,kernel層的實現
// 在核心中發起系統呼叫,執行本應使用者空間發起呼叫的fops函式集,完成引數設定任務
/*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,
}
};*/
static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct snd_pcm_file *pcm_file;
pcm_file = file->private_data;
if (((cmd >> 8) & 0xff) != 'A')
return -ENOTTY;
return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
(void __user *)arg);
}
static int snd_pcm_playback_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
if (snd_BUG_ON(!substream))
return -ENXIO;
if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
return -EINVAL;
return snd_pcm_common_ioctl1(file, substream, cmd, arg);
}
static int snd_pcm_common_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
......
case SNDRV_PCM_IOCTL_PREPARE:
return snd_pcm_prepare(substream, file);
......
}
static int snd_pcm_prepare(struct snd_pcm_substream *substream,
struct file *file)
{
int res;
struct snd_card *card = substream->pcm->card;
int f_flags;
if (file)
f_flags = file->f_flags;
else
f_flags = substream->f_flags;
snd_power_lock(card);
if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
substream, f_flags);
snd_power_unlock(card);
return res;
}
static int snd_pcm_action_nonatomic(struct action_ops *ops,
struct snd_pcm_substream *substream,
int state)
{
int res;
down_read(&snd_pcm_link_rwsem);
if (snd_pcm_stream_linked(substream))
res = snd_pcm_action_group(ops, substream, state, 0);
else
res = snd_pcm_action_single(ops, substream, state);
up_read(&snd_pcm_link_rwsem);
return res;
}
static int snd_pcm_action_single(struct action_ops *ops,
struct snd_pcm_substream *substream,
int state)
{
int res;
res = ops->pre_action(substream, state);
if (res < 0)
return res;
res = ops->do_action(substream, state);
if (res == 0)
ops->post_action(substream, state);
else if (ops->undo_action)
ops->undo_action(substream, state);
return res;
}
/*static struct action_ops snd_pcm_action_prepare = {
.pre_action = snd_pcm_pre_prepare,
.do_action = snd_pcm_do_prepare,
.post_action = snd_pcm_post_prepare
};*/
static int snd_pcm_do_prepare(struct snd_pcm_substream *substream, int state)
{
int err;
err = substream->ops->prepare(substream);
if (err < 0)
return err;
return snd_pcm_do_reset(substream, 0);
}
//復位
static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
runtime->hw_ptr_base = 0;
runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
runtime->status->hw_ptr % runtime->period_size;
runtime->silence_start = runtime->status->hw_ptr;
runtime->silence_filled = 0;
return 0;
}
substream的回撥函式
//pcm subtream的操作函式
/*int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
......
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.ioctl = soc_pcm_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.ioctl = soc_pcm_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;
}
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
......
}*/
static int soc_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai;
int i, ret = 0;
if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
ret = rtd->dai_link->ops->prepare(substream);//呼叫dai link driver的prepare
}
if (platform->driver->ops && platform->driver->ops->prepare) {
ret = platform->driver->ops->prepare(substream);//呼叫platform driver的prepare
}
for (i = 0; i < rtd->num_codecs; i++) {
codec_dai = rtd->codec_dais[i];
if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) {
ret = codec_dai->driver->ops->prepare(substream, codec_dai);//呼叫codec dai driver的prepare
}
}
if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) {
ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);//呼叫cpu dai driver的prepare
}
/* cancel any delayed stream shutdown that is pending */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
rtd->pop_wait) {
rtd->pop_wait = 0;
cancel_delayed_work(&rtd->delayed_work);
}
//把ASoc中的pcm處理部分和dapm進行關聯
snd_soc_dapm_stream_event(rtd, substream->stream, SND_SOC_DAPM_STREAM_START);
for (i = 0; i < rtd->num_codecs; i++)
snd_soc_dai_digital_mute(rtd->codec_dais[i], 0, substream->stream);
out:
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
處理platform、codec-dai、cpu-dai的prepare回撥函式
//dai link的prepare,沒有具體實現
/*static struct snd_soc_ops mt_machine_audio_ops = {
.startup = mtmachine_startup,
.prepare = mtmachine_prepare,
};*/
static int mtmachine_prepare(struct snd_pcm_substream *substream)
{
/* pr_debug("mtmachine_prepare\n"); */
return 0;
}
//platform driver的prepare
static int mtk_pcm_I2S0dl1_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
uint32 MclkDiv3;
uint32 u32AudioI2S = 0;
bool mI2SWLen;
if (mPrepareDone == false) {
SetMemifSubStream(Soc_Aud_Digital_Block_MEM_DL1, substream);
if (runtime->format == SNDRV_PCM_FORMAT_S32_LE ||
runtime->format == SNDRV_PCM_FORMAT_U32_LE) {
SetMemIfFetchFormatPerSample(Soc_Aud_Digital_Block_MEM_DL1,
AFE_WLEN_32_BIT_ALIGN_8BIT_0_24BIT_DATA);
SetoutputConnectionFormat(OUTPUT_DATA_FORMAT_24BIT,
Soc_Aud_InterConnectionOutput_O03);
SetoutputConnectionFormat(OUTPUT_DATA_FORMAT_24BIT,
Soc_Aud_InterConnectionOutput_O04);
SetoutputConnectionFormat(OUTPUT_DATA_FORMAT_24BIT,
Soc_Aud_InterConnectionOutput_O00);
SetoutputConnectionFormat(OUTPUT_DATA_FORMAT_24BIT,
Soc_Aud_InterConnectionOutput_O01);
mI2SWLen = Soc_Aud_I2S_WLEN_WLEN_32BITS;
} else {
SetMemIfFetchFormatPerSample(Soc_Aud_Digital_Block_MEM_DL1, AFE_WLEN_16_BIT);
SetoutputConnectionFormat(OUTPUT_DATA_FORMAT_16BIT,
Soc_Aud_InterConnectionOutput_O03);
SetoutputConnectionFormat(OUTPUT_DATA_FORMAT_16BIT,
Soc_Aud_InterConnectionOutput_O04);
SetoutputConnectionFormat(OUTPUT_DATA_FORMAT_16BIT,
Soc_Aud_InterConnectionOutput_O00);
SetoutputConnectionFormat(OUTPUT_DATA_FORMAT_16BIT,
Soc_Aud_InterConnectionOutput_O01);
mI2SWLen = Soc_Aud_I2S_WLEN_WLEN_16BITS;
}
SetSampleRate(Soc_Aud_Digital_Block_MEM_I2S, runtime->rate);
/* I2S out Setting */
u32AudioI2S = SampleRateTransform(runtime->rate) << 8;
u32AudioI2S |= Soc_Aud_I2S_FORMAT_I2S << 3; /* us3 I2s format */
u32AudioI2S |= Soc_Aud_I2S_WLEN_WLEN_32BITS << 1; /* 32bit */
if (mI2S0dl1_hdoutput_control == true) {
/* here to open APLL */
if (!mtk_soc_always_hd)
EnableALLbySampleRate(runtime->rate);
MclkDiv3 = SetCLkMclk(Soc_Aud_I2S1, runtime->rate); /* select I2S */
MclkDiv3 = SetCLkMclk(Soc_Aud_I2S3, runtime->rate); /* select I2S */
u32AudioI2S |= Soc_Aud_LOW_JITTER_CLOCK << 12; /* Low jitter mode */
} else
u32AudioI2S &= ~(Soc_Aud_LOW_JITTER_CLOCK << 12);
#ifdef CONFIG_MTK_SMARTPA_SUPPORT
/* use low jitter mode for smart pa */
u32AudioI2S |= Soc_Aud_LOW_JITTER_CLOCK << 12; /* Low jitter mode */
#endif
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_2) == false) {
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_2, true);
Afe_Set_Reg(AFE_I2S_CON3, u32AudioI2S | 1, AFE_MASK_ALL);
} else
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_2, true);
/* start I2S DAC out */
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC) == false) {
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, true);
SetI2SDacOut(substream->runtime->rate, mI2S0dl1_hdoutput_control, mI2SWLen);
SetI2SDacEnable(true);
} else
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, true);
if (mI2S0dl1_hdoutput_control == true) {
EnableI2SDivPower(AUDIO_APLL12_DIV2, true);
EnableI2SDivPower(AUDIO_APLL12_DIV4, true);
}
EnableAfe(true);
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC) == true)
SetI2SADDAEnable(true);
mPrepareDone = true;
}
return 0;
}
//codec dai driver的prepare
/*static const struct snd_soc_dai_ops mt6323_aif1_dai_ops = {
.startup = mt63xx_codec_startup,
.prepare = mt63xx_codec_prepare,
.trigger = mt6323_codec_trigger,
};*/
static int mt63xx_codec_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *Daiport)
{
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
mBlockSampleRate[AUDIO_ANALOG_DEVICE_IN_ADC] = substream->runtime->rate;
} else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
mBlockSampleRate[AUDIO_ANALOG_DEVICE_OUT_DAC] = substream->runtime->rate;
}
return 0;
}
//cpu dai driver的prepare,沒有實現prepare
/*static struct snd_soc_dai_ops mtk_dai_stub_ops = {
.startup = multimedia_startup,
};*/
發生stream事件時,會觸發snd_soc_dapm_stream_even。什麼叫stream事件?
準備或關閉一個pcm stream通道(snd_pcm_prepare/snd_pcm_close)這些都屬於stream事件。
另外suspend或resume時,也會觸發snd_soc_dapm_stream_event處理。
void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
int event)
{
struct snd_soc_card *card = rtd->card;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
soc_dapm_stream_event(rtd, stream, event);
mutex_unlock(&card->dapm_mutex);
}
static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
int event)
{
int i;
soc_dapm_dai_stream_event(rtd->cpu_dai, stream, event);
//遍歷codec每個dapm widget,如果該widget的stream name與傳遞進來的stream引數相匹配,如果匹配則置widget->active為真 。
for (i = 0; i < rtd->num_codecs; i++)
soc_dapm_dai_stream_event(rtd->codec_dais[i], stream, event);
dapm_power_widgets(rtd->card, event);//觸發dapm,重置相關的widgets
}