Android音频驱动-ASOC之PCM Prepare

来源:互联网 发布:森林法则知乎 编辑:程序博客网 时间:2024/05/20 01:08

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的方式来调用kernelint 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的preparestatic 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}