1. 程式人生 > >Android音訊驅動-ASOC之PCM Prepare

Android音訊驅動-ASOC之PCM Prepare

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
}