Android音頻驅動-ASOC之PCM Open

status_t AudioALSAPlaybackHandlerBase::openPcmDriver(const unsigned int device)
{
    mPcm = pcm_open(AudioALSADeviceParser::getInstance()->GetCardIndex(),
                    device, PCM_OUT | PCM_MONOTONIC, &mConfig);
    return NO_ERROR;
}
struct pcm *pcm_open(unsigned int card, unsigned int device,
                     unsigned int flags, struct pcm_config *config)
{
    struct pcm *pcm;
    struct snd_pcm_info info;
    struct snd_pcm_hw_params params;
    struct snd_pcm_sw_params sparams;
    char fn[256];
    int rc;

    pcm = calloc(1, sizeof(struct pcm));
    pcm->config = *config;

    snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
             flags & PCM_IN ? 'c' : 'p');

    pcm->flags = flags;
    pcm->fd = open(fn, O_RDWR);
    ......
}
//在創建alsa主設備時設置了文件操作對象,PCM設備都是alsa的從設備,共享一個驅動,打開次設備時會調用主設備的open函數
/*static const struct file_operations snd_fops =
{
    .owner =    THIS_MODULE,
    .open =     snd_open,
    .llseek =   noop_llseek,
};*/
static int snd_open(struct inode *inode, struct file *file)
{
    unsigned int minor = iminor(inode);
    struct snd_minor *mptr = NULL;
    const struct file_operations *new_fops;
    int err = 0;
    mptr = snd_minors[minor];
    new_fops = fops_get(mptr->f_ops);
    replace_fops(file, new_fops);
    if (file->f_op->open)
        err = file->f_op->open(inode, file);
    return err;
}
//註冊PCM設備時設置了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,
    }
};*/
static int snd_pcm_playback_open(struct inode *inode, struct file *file)
{
    struct snd_pcm *pcm;
    int err = nonseekable_open(inode, file);
    pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
    err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
    return err;
}
void *snd_lookup_minor_data(unsigned int minor, int type)
{
    struct snd_minor *mreg;
    void *private_data;

    if (minor >= ARRAY_SIZE(snd_minors))
        return NULL;
    mreg = snd_minors[minor];
    if (mreg && mreg->type == type) {
        private_data = mreg->private_data;
        if (private_data && mreg->card_ptr)
            get_device(&mreg->card_ptr->card_dev);
    } else
        private_data = NULL;
    return private_data;
}
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
    int err;
    err = snd_card_file_add(pcm->card, file);//將file加入到card的files鏈表進行管理
    while (1) {
        err = snd_pcm_open_file(file, pcm, stream);
    }
    return err;
}
static int snd_pcm_open_file(struct file *file,
                 struct snd_pcm *pcm,
                 int stream)
{
    struct snd_pcm_file *pcm_file;
    struct snd_pcm_substream *substream;
    int err;

    err = snd_pcm_open_substream(pcm, stream, file, &substream);
    pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
    pcm_file->substream = substream;//將打開的substream保存在pcm_file中
    if (substream->ref_count == 1) {
        substream->file = pcm_file;
        substream->pcm_release = pcm_release_private;
    }
    file->private_data = pcm_file;//file獲得打開的substream
    return 0;
}
//打開pcm所屬的substream,需要使用substream->ops操作函數
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
               struct file *file,
               struct snd_pcm_substream **rsubstream)
{
    struct snd_pcm_substream *substream;
    int err;

    err = snd_pcm_attach_substream(pcm, stream, file, &substream);//獲取pcm的substream
    err = snd_pcm_hw_constraints_init(substream);//初始化pcm硬件約束
    if ((err = substream->ops->open(substream)) < 0)//調用substream的回調函數open
        goto error;

    substream->hw_opened = 1;
    err = snd_pcm_hw_constraints_complete(substream);//設置pcm硬件約束
    *rsubstream = substream;
    return 0;
}
int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
                 struct file *file,
                 struct snd_pcm_substream **rsubstream)
{
    struct snd_pcm_str * pstr;
    struct snd_pcm_substream *substream;
    struct snd_pcm_runtime *runtime;
    struct snd_ctl_file *kctl;
    struct snd_card *card;
    int prefer_subdevice = -1;
    size_t size;

    *rsubstream = NULL;
    pstr = &pcm->streams[stream];
    card = pcm->card;

    list_for_each_entry(kctl, &card->ctl_files, list) {
        if (kctl->pid == task_pid(current)) {
            prefer_subdevice = kctl->prefer_pcm_subdevice;
            if (prefer_subdevice != -1)
                break;
        }
    }

    switch (stream) {
    case SNDRV_PCM_STREAM_PLAYBACK:
        if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
            for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; 
                 substream = substream->next) {
                if (SUBSTREAM_BUSY(substream))
                    return -EAGAIN;
            }
        }
        break;
    case SNDRV_PCM_STREAM_CAPTURE:
        if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
            for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; 
                 substream = substream->next) {
                if (SUBSTREAM_BUSY(substream))
                    return -EAGAIN;
            }
        }
        break;
    default:
        return -EINVAL;
    }

    if (file->f_flags & O_APPEND) {
        if (prefer_subdevice < 0) {
            if (pstr->substream_count > 1)
                return -EINVAL; /* must be unique */
            substream = pstr->substream;
        } else {
            for (substream = pstr->substream; substream;
                 substream = substream->next)
                if (substream->number == prefer_subdevice)
                    break;
        }

        substream->ref_count++;
        *rsubstream = substream;
        return 0;
    }

    if (prefer_subdevice >= 0) {
        for (substream = pstr->substream; substream; substream = substream->next)
            if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice)
                goto __ok;
    }
    for (substream = pstr->substream; substream; substream = substream->next)
        if (!SUBSTREAM_BUSY(substream))
            break;
      __ok:
    runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);

    size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status));
    runtime->status = snd_malloc_pages(size, GFP_KERNEL);
    memset((void*)runtime->status, 0, size);

    size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control));
    runtime->control = snd_malloc_pages(size, GFP_KERNEL);
    memset((void*)runtime->control, 0, size);

    runtime->status->state = SNDRV_PCM_STATE_OPEN;

    substream->runtime = runtime;
    substream->private_data = pcm->private_data;
    substream->ref_count = 1;
    substream->pid = get_pid(task_pid(current));
    pstr->substream_opened++;
    *rsubstream = substream;
    return 0;
}

在創建pcm設備時已經調用snd_pcm_set_ops函數設置了substream->ops,在打開pcm的時候需要調用substream的ops->open函數

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;

    /* startup the audio subsystem */
    if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) {
        ret = cpu_dai->driver->ops->startup(substream, cpu_dai);//調用cpu dai driver的startup
    }

    if (platform->driver->ops && platform->driver->ops->open) {
        ret = platform->driver->ops->open(substream);//調用platform driver的open
    }

    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);//調用codec dai driver的startup
        }
    }

    if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
        ret = rtd->dai_link->ops->startup(substream);//調用dai link的startup,沒有實現
    }

    /* Check that the codec and cpu DAIs are compatible */
    soc_pcm_init_runtime_hw(substream);//檢測codec和cup dai的兼容性

    return ret;
}

cpu dai driver、platform driver、codec dai driver的具體實現

static int multimedia_startup(struct snd_pcm_substream *substream,
                  struct snd_soc_dai *dai)
{
    snd_pcm_hw_constraint_list(substream->runtime, 0,
                   SNDRV_PCM_HW_PARAM_RATE,
                   &constraints_sample_rates);
    return 0;
}
static int mtk_pcm_I2S0dl1_open(struct snd_pcm_substream *substream)
{
    int ret = 0;
    struct snd_pcm_runtime *runtime = substream->runtime;

    AfeControlSramLock();
    if (GetSramState() == SRAM_STATE_FREE) {
        mtk_I2S0dl1_hardware.buffer_bytes_max = GetPLaybackSramFullSize();
        mPlaybackSramState = SRAM_STATE_PLAYBACKFULL;
        SetSramState(mPlaybackSramState);
    } else {
        mtk_I2S0dl1_hardware.buffer_bytes_max = GetPLaybackDramSize();
        mPlaybackSramState = SRAM_STATE_PLAYBACKDRAM;
    }
    AfeControlSramUnLock();
    if (mPlaybackSramState == SRAM_STATE_PLAYBACKDRAM)
        AudDrv_Emi_Clk_On();

    runtime->hw = mtk_I2S0dl1_hardware;

    AudDrv_Clk_On();
    memcpy((void *)(&(runtime->hw)), (void *)&mtk_I2S0dl1_hardware ,
           sizeof(struct snd_pcm_hardware));
    pI2S0dl1MemControl = Get_Mem_ControlT(Soc_Aud_Digital_Block_MEM_DL1);

    ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                     &constraints_sample_rates);

    return 0;
}
static int mt63xx_codec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *Daiport)
{
    if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && substream->runtime->rate) {
        mBlockSampleRate[AUDIO_ANALOG_DEVICE_IN_ADC] = substream->runtime->rate;

    } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && substream->runtime->rate) {
        mBlockSampleRate[AUDIO_ANALOG_DEVICE_OUT_DAC] = substream->runtime->rate;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章