Android音频驱动-ASOC之PCM HW Params

来源:互联网 发布:java modbus与rs485 编辑:程序博客网 时间:2024/05/20 05:27

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;}
原创粉丝点击