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

Android音訊驅動-ASOC之PCM HW Params

ALSA的HW_param流程

soc_pcm_hw_params => rtd->dai_link->ops->hw_params => codec_dai->driver->ops->hw_params =>
cpu_dai->driver->ops->hw_params => platform->driver->ops->hw_params

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)); if (!pcm || !config) return &bad_pcm; /* TODO: could support default config here */ 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
); if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) { oops(pcm, errno, "cannot get info"); goto fail_close; } param_init(&params); param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT, pcm_format_to_alsa(config->format)); param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT
, SNDRV_PCM_SUBFORMAT_STD); param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size); param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, pcm_format_to_bits(config->format)); param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS, pcm_format_to_bits(config->format) * config->channels); param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS, config->channels); param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count); param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate); param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) { oops(pcm, errno, "cannot set hw params"); goto fail_close; } /* get our refined hw_params */ config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS); pcm->buffer_size = config->period_count * config->period_size; memset(&sparams, 0, sizeof(sparams)); sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE; sparams.period_step = 1; if (!config->start_threshold) { if (pcm->flags & PCM_IN) pcm->config.start_threshold = sparams.start_threshold = 1; else pcm->config.start_threshold = sparams.start_threshold = config->period_count * config->period_size / 2; } else sparams.start_threshold = config->start_threshold; /* pick a high stop threshold - todo: does this need further tuning */ if (!config->stop_threshold) { if (pcm->flags & PCM_IN) pcm->config.stop_threshold = sparams.stop_threshold = config->period_count * config->period_size * 10; else pcm->config.stop_threshold = sparams.stop_threshold = config->period_count * config->period_size; } else sparams.stop_threshold = config->stop_threshold; if (!pcm->config.avail_min) { if (pcm->flags & PCM_MMAP) pcm->config.avail_min = sparams.avail_min = pcm->config.period_size; else pcm->config.avail_min = sparams.avail_min = 1; } else sparams.avail_min = config->avail_min; sparams.xfer_align = config->period_size / 2; /* needed for old kernels */ sparams.silence_threshold = config->silence_threshold; sparams.silence_size = config->silence_size; pcm->boundary = sparams.boundary = pcm->buffer_size; while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size) pcm->boundary *= 2; if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) { oops(pcm, errno, "cannot set sw params"); goto fail; } rc = pcm_hw_mmap_status(pcm); pcm->underruns = 0; return pcm; } static int snd_pcm_common_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { switch (cmd) { case SNDRV_PCM_IOCTL_HW_PARAMS: return snd_pcm_hw_params_user(substream, arg); }
static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
                  struct snd_pcm_hw_params __user * _params)
{
    struct snd_pcm_hw_params *params;
    int err;

    params = memdup_user(_params, sizeof(*params));

    err = snd_pcm_hw_params(substream, params);
    if (copy_to_user(_params, params, sizeof(*params))) {
        if (!err)
            err = -EFAULT;
    }

    kfree(params);
    return err;
}
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
                 struct snd_pcm_hw_params *params)
{
    struct snd_pcm_runtime *runtime;
    int err, usecs;
    unsigned int bits;
    snd_pcm_uframes_t frames;

    runtime = substream->runtime;
    snd_pcm_stream_lock_irq(substream);
    switch (runtime->status->state) {
    case SNDRV_PCM_STATE_OPEN:
    case SNDRV_PCM_STATE_SETUP:
    case SNDRV_PCM_STATE_PREPARED:
        break;
    default:
        snd_pcm_stream_unlock_irq(substream);
        return -EBADFD;
    }
    snd_pcm_stream_unlock_irq(substream);
    if (atomic_read(&substream->mmap_count))
        return -EBADFD;

    params->rmask = ~0U;
    err = snd_pcm_hw_refine(substream, params);

    err = snd_pcm_hw_params_choose(substream, params);

    if (substream->ops->hw_params != NULL) {
        err = substream->ops->hw_params(substream, params);
    }

    runtime->access = params_access(params);
    runtime->format = params_format(params);
    runtime->subformat = params_subformat(params);
    runtime->channels = params_channels(params);
    runtime->rate = params_rate(params);
    runtime->period_size = params_period_size(params);
    runtime->periods = params_periods(params);
    runtime->buffer_size = params_buffer_size(params);
    runtime->info = params->info;
    runtime->rate_num = params->rate_num;
    runtime->rate_den = params->rate_den;
    runtime->no_period_wakeup =
            (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
            (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);

    bits = snd_pcm_format_physical_width(runtime->format);
    runtime->sample_bits = bits;
    bits *= runtime->channels;
    runtime->frame_bits = bits;
    frames = 1;
    while (bits % 8 != 0) {
        bits *= 2;
        frames *= 2;
    }
    runtime->byte_align = bits / 8;
    runtime->min_align = frames;

    /* Default sw params */
    runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
    runtime->period_step = 1;
    runtime->control->avail_min = runtime->period_size;
    runtime->start_threshold = 1;
    runtime->stop_threshold = runtime->buffer_size;
    runtime->silence_threshold = 0;
    runtime->silence_size = 0;
    runtime->boundary = runtime->buffer_size;
    while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
        runtime->boundary *= 2;

    snd_pcm_timer_resolution_change(substream);
    snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP);

    if (pm_qos_request_active(&substream->latency_pm_qos_req))
        pm_qos_remove_request(&substream->latency_pm_qos_req);
    if ((usecs = period_to_usecs(runtime)) >= 0)
        pm_qos_add_request(&substream->latency_pm_qos_req,
                   PM_QOS_CPU_DMA_LATENCY, usecs);
    return 0;
}
static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                struct snd_pcm_hw_params *params)
{
    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;
    int i, ret = 0;

    mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);

    ret = soc_pcm_params_symmetry(substream, params);

    if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
            //dai link driver沒有實現hw_params
        ret = rtd->dai_link->ops->hw_params(substream, params);
    }

    for (i = 0; i < rtd->num_codecs; i++) {
        struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
        struct snd_pcm_hw_params codec_params;

        /* copy params for each codec */
        codec_params = *params;

        /* fixup params based on TDM slot masks */
        if (codec_dai->tx_mask)
            soc_pcm_codec_params_fixup(&codec_params, codec_dai->tx_mask);
        if (codec_dai->rx_mask)
            soc_pcm_codec_params_fixup(&codec_params, codec_dai->rx_mask);
                //codec dai driver沒有實現hw_params
        ret = soc_dai_hw_params(substream, &codec_params, codec_dai);

        codec_dai->rate = params_rate(&codec_params);
        codec_dai->channels = params_channels(&codec_params);
        codec_dai->sample_bits = snd_pcm_format_physical_width(
                        params_format(&codec_params));
    }
        //cpu dai driver沒有實現hw_params
    ret = soc_dai_hw_params(substream, params, cpu_dai);

    if (platform->driver->ops && platform->driver->ops->hw_params) {
            //呼叫platform driver的hw_params函式
        ret = platform->driver->ops->hw_params(substream, params);
    }

    /* store the parameters for each DAIs */
    cpu_dai->rate = params_rate(params);
    cpu_dai->channels = params_channels(params);
    cpu_dai->sample_bits =
        snd_pcm_format_physical_width(params_format(params));

out:
    mutex_unlock(&rtd->pcm_mutex);
    return ret;
}
//呼叫cpu dai driver和codec dai driver的具體實現
int soc_dai_hw_params(struct snd_pcm_substream *substream,
              struct snd_pcm_hw_params *params,
              struct snd_soc_dai *dai)
{
    int ret;
    if (dai->driver->ops && dai->driver->ops->hw_params) {
        ret = dai->driver->ops->hw_params(substream, params, dai);
    }
    return 0;
}

platform driver例項的實現

static int mtk_pcm_I2S0dl1_hw_params(struct snd_pcm_substream *substream,
                     struct snd_pcm_hw_params *hw_params)
{
    int ret = 0;

    substream->runtime->dma_bytes = params_buffer_bytes(hw_params);

    if (mPlaybackSramState == SRAM_STATE_PLAYBACKFULL) {
        substream->runtime->dma_area = (unsigned char *)Get_Afe_SramBase_Pointer();
        substream->runtime->dma_addr = AFE_INTERNAL_SRAM_PHY_BASE;
        SetHighAddr(Soc_Aud_Digital_Block_MEM_DL1, false);
        AudDrv_Allocate_DL1_Buffer(mDev, substream->runtime->dma_bytes);
    } else {
        substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
        substream->runtime->dma_area = Dl1_Playback_dma_buf->area;
        substream->runtime->dma_addr = Dl1_Playback_dma_buf->addr;
        SetHighAddr(Soc_Aud_Digital_Block_MEM_DL1, true);
        SetDL1Buffer(substream, hw_params);
    }
    return ret;
}