static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream, unsigned int channels) { int err; substream->runtime->hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_FIFO_IN_FRAMES; substream->runtime->hw.formats = ua->format_bit; substream->runtime->hw.rates = snd_pcm_rate_to_rate_bit(ua->rate); substream->runtime->hw.rate_min = ua->rate; substream->runtime->hw.rate_max = ua->rate; substream->runtime->hw.channels_min = channels; substream->runtime->hw.channels_max = channels; substream->runtime->hw.buffer_bytes_max = 45000 * 1024; substream->runtime->hw.period_bytes_min = 1; substream->runtime->hw.period_bytes_max = UINT_MAX; substream->runtime->hw.periods_min = 2; substream->runtime->hw.periods_max = UINT_MAX; err = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1500000 / ua->packets_per_second, UINT_MAX); if (err < 0) return err; err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); return err; }
/* * parse the format descriptor and stores the possible sample rates * on the audioformat table (audio class v1). * * @dev: usb device * @fp: audioformat record * @fmt: the format descriptor * @offset: the start offset of descriptor pointing the rate type * (7 for type I and II, 8 for type II) */ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audioformat *fp, unsigned char *fmt, int offset) { int nr_rates = fmt[offset]; if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n", chip->dev->devnum, fp->iface, fp->altsetting); return -1; } if (nr_rates) { /* * build the rate table and bitmap flags */ int r, idx; fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); if (fp->rate_table == NULL) { snd_printk(KERN_ERR "cannot malloc\n"); return -1; } fp->nr_rates = 0; fp->rate_min = fp->rate_max = 0; for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) { unsigned int rate = combine_triple(&fmt[idx]); if (!rate) continue; /* C-Media CM6501 mislabels its 96 kHz altsetting */ if (rate == 48000 && nr_rates == 1 && (chip->usb_id == USB_ID(0x0d8c, 0x0201) || chip->usb_id == USB_ID(0x0d8c, 0x0102)) && fp->altsetting == 5 && fp->maxpacksize == 392) rate = 96000; /* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */ if (rate == 16000 && chip->usb_id == USB_ID(0x041e, 0x4068)) rate = 8000; fp->rate_table[fp->nr_rates] = rate; if (!fp->rate_min || rate < fp->rate_min) fp->rate_min = rate; if (!fp->rate_max || rate > fp->rate_max) fp->rate_max = rate; fp->rates |= snd_pcm_rate_to_rate_bit(rate); fp->nr_rates++; } if (!fp->nr_rates) { hwc_debug("All rates were zero. Skipping format!\n"); return -1; } } else { /* continuous rates */ fp->rates = SNDRV_PCM_RATE_CONTINUOUS; fp->rate_min = combine_triple(&fmt[offset + 1]); fp->rate_max = combine_triple(&fmt[offset + 4]); } return 0; }
/* * Helper function to walk the array of sample rate triplets reported by * the device. The problem is that we need to parse whole array first to * get to know how many sample rates we have to expect. * Then fp->rate_table can be allocated and filled. */ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, struct audioformat *fp, int nr_triplets, const unsigned char *data) { int i, nr_rates = 0; fp->rates = fp->rate_min = fp->rate_max = 0; for (i = 0; i < nr_triplets; i++) { int min = combine_quad(&data[2 + 12 * i]); int max = combine_quad(&data[6 + 12 * i]); int res = combine_quad(&data[10 + 12 * i]); unsigned int rate; if ((max < 0) || (min < 0) || (res < 0) || (max < min)) continue; /* * for ranges with res == 1, we announce a continuous sample * rate range, and this function should return 0 for no further * parsing. */ if (res == 1) { fp->rate_min = min; fp->rate_max = max; fp->rates = SNDRV_PCM_RATE_CONTINUOUS; return 0; } for (rate = min; rate <= max; rate += res) { if (fp->rate_table) fp->rate_table[nr_rates] = rate; if (!fp->rate_min || rate < fp->rate_min) fp->rate_min = rate; if (!fp->rate_max || rate > fp->rate_max) fp->rate_max = rate; fp->rates |= snd_pcm_rate_to_rate_bit(rate); nr_rates++; if (nr_rates >= MAX_NR_RATES) { usb_audio_err(chip, "invalid uac2 rates\n"); break; } /* avoid endless loop */ if (res == 0) break; } } return nr_rates; }
static int snd_pmac_pcm_open(struct snd_pmac *chip, struct pmac_stream *rec, struct snd_pcm_substream *subs) { struct snd_pcm_runtime *runtime = subs->runtime; int i; /* look up frequency table and fill bit mask */ runtime->hw.rates = 0; for (i = 0; i < chip->num_freqs; i++) if (chip->freqs_ok & (1 << i)) runtime->hw.rates |= snd_pcm_rate_to_rate_bit(chip->freq_table[i]); /* check for minimum and maximum rates */ for (i = 0; i < chip->num_freqs; i++) { if (chip->freqs_ok & (1 << i)) { runtime->hw.rate_max = chip->freq_table[i]; break; } } for (i = chip->num_freqs - 1; i >= 0; i--) { if (chip->freqs_ok & (1 << i)) { runtime->hw.rate_min = chip->freq_table[i]; break; } } runtime->hw.formats = chip->formats_ok; if (chip->can_capture) { if (! chip->can_duplex) runtime->hw.info |= SNDRV_PCM_INFO_HALF_DUPLEX; runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; } runtime->private_data = rec; rec->substream = subs; #if 0 /* FIXME: still under development.. */ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_pmac_hw_rule_rate, chip, rec->stream, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, snd_pmac_hw_rule_format, chip, rec->stream, -1); #endif runtime->hw.periods_max = rec->cmd.size - 1; /* constraints to fix choppy sound */ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); return 0; }
/** * STA381xx_set_dai_sysclk - configure MCLK * @codec_dai: the codec DAI * @clk_id: the clock ID (ignored) * @freq: the MCLK input frequency * @dir: the clock direction (ignored) * * The value of MCLK is used to determine which sample rates are supported * by the STA381xx, based on the mclk_ratios table. * * This function must be called by the machine driver's 'startup' function, * otherwise the list of supported sample rates will not be available in * time for ALSA. * * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause * theoretically possible sample rates to be enabled. Call it again with a * proper value set one the external clock is set (most probably you would do * that from a machine's driver 'hw_param' hook. */ static int STA381xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; struct STA381xx_priv *STA381xx = snd_soc_codec_get_drvdata(codec); int i, j, ir, fs; unsigned int rates = 0; unsigned int rate_min = -1; unsigned int rate_max = 0; CODEC_DEBUG("~~~~%s\n", __func__); pr_debug("mclk=%u\n", freq); STA381xx->mclk = freq; if (STA381xx->mclk) { for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) { ir = interpolation_ratios[i].ir; fs = interpolation_ratios[i].fs; for (j = 0; mclk_ratios[ir][j].ratio; j++) { if (mclk_ratios[ir][j].ratio * fs == freq) { rates |= snd_pcm_rate_to_rate_bit(fs); if (fs < rate_min) rate_min = fs; if (fs > rate_max) rate_max = fs; break; } } } /* FIXME: soc should support a rate list */ rates &= ~SNDRV_PCM_RATE_KNOT; if (!rates) { dev_err(codec->dev, "could not find a valid sample rate\n"); return -EINVAL; } } else { /* enable all possible rates */ rates = STA381xx_RATES; rate_min = 32000; rate_max = 192000; } codec_dai->driver->playback.rates = rates; codec_dai->driver->playback.rate_min = rate_min; codec_dai->driver->playback.rate_max = rate_max; return 0; }
/** * cs4270_set_dai_sysclk - determine the CS4270 samples rates. * @codec_dai: the codec DAI * @clk_id: the clock ID (ignored) * @freq: the MCLK input frequency * @dir: the clock direction (ignored) * * This function is used to tell the codec driver what the input MCLK * frequency is. * * The value of MCLK is used to determine which sample rates are supported * by the CS4270. The ratio of MCLK / Fs must be equal to one of nine * supported values - 64, 96, 128, 192, 256, 384, 512, 768, and 1024. * * This function calculates the nine ratios and determines which ones match * a standard sample rate. If there's a match, then it is added to the list * of supported sample rates. * * This function must be called by the machine driver's 'startup' function, * otherwise the list of supported sample rates will not be available in * time for ALSA. * * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause * theoretically possible sample rates to be enabled. Call it again with a * proper value set one the external clock is set (most probably you would do * that from a machine's driver 'hw_param' hook. */ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; struct cs4270_private *cs4270 = codec->private_data; unsigned int rates = 0; unsigned int rate_min = -1; unsigned int rate_max = 0; unsigned int i; cs4270->mclk = freq; if (cs4270->mclk) { for (i = 0; i < NUM_MCLK_RATIOS; i++) { unsigned int rate = freq / cs4270_mode_ratios[i].ratio; rates |= snd_pcm_rate_to_rate_bit(rate); if (rate < rate_min) rate_min = rate; if (rate > rate_max) rate_max = rate; } /* FIXME: soc should support a rate list */ rates &= ~SNDRV_PCM_RATE_KNOT; if (!rates) { dev_err(codec->dev, "could not find a valid sample rate\n"); return -EINVAL; } } else { /* enable all possible rates */ rates = SNDRV_PCM_RATE_8000_192000; rate_min = 8000; rate_max = 192000; } codec_dai->playback.rates = rates; codec_dai->playback.rate_min = rate_min; codec_dai->playback.rate_max = rate_max; codec_dai->capture.rates = rates; codec_dai->capture.rate_min = rate_min; codec_dai->capture.rate_max = rate_max; return 0; }
static int limit_channels_and_rates(struct snd_dice *dice, struct snd_pcm_runtime *runtime, enum amdtp_stream_direction dir, unsigned int index, unsigned int size) { struct snd_pcm_hardware *hw = &runtime->hw; struct amdtp_stream *stream; unsigned int rate; __be32 reg; int err; /* * Retrieve current Multi Bit Linear Audio data channel and limit to * it. */ if (dir == AMDTP_IN_STREAM) { stream = &dice->tx_stream[index]; err = snd_dice_transaction_read_tx(dice, size * index + TX_NUMBER_AUDIO, ®, sizeof(reg)); } else { stream = &dice->rx_stream[index]; err = snd_dice_transaction_read_rx(dice, size * index + RX_NUMBER_AUDIO, ®, sizeof(reg)); } if (err < 0) return err; hw->channels_min = hw->channels_max = be32_to_cpu(reg); /* Retrieve current sampling transfer frequency and limit to it. */ err = snd_dice_transaction_get_rate(dice, &rate); if (err < 0) return err; hw->rates = snd_pcm_rate_to_rate_bit(rate); snd_pcm_limit_hw_rates(runtime); return 0; }
/* * Determine the CS4270 samples rates. * * 'freq' is the input frequency to MCLK. The other parameters are ignored. * * The value of MCLK is used to determine which sample rates are supported * by the CS4270. The ratio of MCLK / Fs must be equal to one of nine * support values: 64, 96, 128, 192, 256, 384, 512, 768, and 1024. * * This function calculates the nine ratios and determines which ones match * a standard sample rate. If there's a match, then it is added to the list * of support sample rates. * * This function must be called by the machine driver's 'startup' function, * otherwise the list of supported sample rates will not be available in * time for ALSA. * * Note that in stand-alone mode, the sample rate is determined by input * pins M0, M1, MDIV1, and MDIV2. Also in stand-alone mode, divide-by-3 * is not a programmable option. However, divide-by-3 is not an available * option in stand-alone mode. This cases two problems: a ratio of 768 is * not available (it requires divide-by-3) and B) ratios 192 and 384 can * only be selected with divide-by-1.5, but there is an errate that make * this selection difficult. * * In addition, there is no mechanism for communicating with the machine * driver what the input settings can be. This would need to be implemented * for stand-alone mode to work. */ static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; struct cs4270_private *cs4270 = codec->private_data; unsigned int rates = 0; unsigned int rate_min = -1; unsigned int rate_max = 0; unsigned int i; cs4270->mclk = freq; for (i = 0; i < NUM_MCLK_RATIOS; i++) { unsigned int rate = freq / mclk_ratios[i]; rates |= snd_pcm_rate_to_rate_bit(rate); if (rate < rate_min) rate_min = rate; if (rate > rate_max) rate_max = rate; } /* FIXME: soc should support a rate list */ rates &= ~SNDRV_PCM_RATE_KNOT; if (!rates) { printk(KERN_ERR "cs4270: could not find a valid sample rate\n"); return -EINVAL; } codec_dai->playback.rates = rates; codec_dai->playback.rate_min = rate_min; codec_dai->playback.rate_max = rate_max; codec_dai->capture.rates = rates; codec_dai->capture.rate_min = rate_min; codec_dai->capture.rate_max = rate_max; return 0; }
/* * codec driver */ static int es8316_probe(struct platform_device *pdev) { struct nxp_snd_dai_plat_data *plat = pdev->dev.platform_data; struct snd_soc_card *card = &es8316_card; struct snd_soc_jack_gpio *jack = &jack_gpio; struct snd_soc_dai_driver *i2s_dai = NULL; struct nxp_snd_jack_pin *hpin = NULL; unsigned int rates = 0, format = 0; int ret; /* set I2S name */ if (plat) sprintf(str_dai_name, "%s.%d", DEV_NAME_I2S, plat->i2s_ch); if (plat) { rates = plat->sample_rate; format = plat->pcm_format; hpin = &plat->hp_jack; if (hpin->support) { jack->gpio = hpin->detect_io; jack->invert = hpin->detect_level ? false : true; jack->debounce_time = hpin->debounce_time ? hpin->debounce_time : 200; } else { jack->name = NULL; } } #if defined (CFG_IO_AUDIO_AMP_POWER) gpio_request(AUDIO_AMP_POWER, "es8316_amp_en"); gpio_direction_output(AUDIO_AMP_POWER, 0); #endif card->dev = &pdev->dev; ret = snd_soc_register_card(card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } if (card->rtd) { struct snd_soc_dai *cpu_dai = card->rtd->cpu_dai; if (cpu_dai) i2s_dai = cpu_dai->driver; } pr_debug("es8316-dai: register card %s -> %s\n", card->dai_link->codec_dai_name, card->dai_link->cpu_dai_name); if (NULL == i2s_dai) return 0; /* * Reset i2s sample rates */ if (rates) { rates = snd_pcm_rate_to_rate_bit(rates); if (SNDRV_PCM_RATE_KNOT == rates) printk("%s, invalid sample rates=%d\n", __func__, plat->sample_rate); else { i2s_dai->playback.rates = rates; i2s_dai->capture.rates = rates; } } /* * Reset i2s format */ if (format) { i2s_dai->playback.formats = format; i2s_dai->capture.formats = format; } return ret; }
static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_pcm_substream *substream = rule->private; struct snd_dice *dice = substream->private_data; const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval rates = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i, rate, mode, *pcm_channels; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) pcm_channels = dice->tx_channels; else pcm_channels = dice->rx_channels; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) continue; if (!snd_interval_test(c, pcm_channels[mode])) continue; rates.min = min(rates.min, rate); rates.max = max(rates.max, rate); } return snd_interval_refine(r, &rates); } static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_pcm_substream *substream = rule->private; struct snd_dice *dice = substream->private_data; const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval channels = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i, rate, mode, *pcm_channels; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) pcm_channels = dice->tx_channels; else pcm_channels = dice->rx_channels; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) continue; if (!snd_interval_test(r, rate)) continue; channels.min = min(channels.min, pcm_channels[mode]); channels.max = max(channels.max, pcm_channels[mode]); } return snd_interval_refine(c, &channels); } static void limit_channels_and_rates(struct snd_dice *dice, struct snd_pcm_runtime *runtime, unsigned int *pcm_channels) { struct snd_pcm_hardware *hw = &runtime->hw; unsigned int i, rate, mode; hw->channels_min = UINT_MAX; hw->channels_max = 0; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) continue; hw->rates |= snd_pcm_rate_to_rate_bit(rate); if (pcm_channels[mode] == 0) continue; hw->channels_min = min(hw->channels_min, pcm_channels[mode]); hw->channels_max = max(hw->channels_max, pcm_channels[mode]); } snd_pcm_limit_hw_rates(runtime); } static void limit_period_and_buffer(struct snd_pcm_hardware *hw) { hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ hw->periods_max = UINT_MAX; hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */ /* Just to prevent from allocating much pages. */ hw->period_bytes_max = hw->period_bytes_min * 2048; hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; } static int init_hw_info(struct snd_dice *dice, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; struct amdtp_stream *stream; unsigned int *pcm_channels; int err; hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_BLOCK_TRANSFER; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AMDTP_IN_PCM_FORMAT_BITS; stream = &dice->tx_stream; pcm_channels = dice->tx_channels; } else { hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; stream = &dice->rx_stream; pcm_channels = dice->rx_channels; } limit_channels_and_rates(dice, runtime, pcm_channels); limit_period_and_buffer(hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, dice_rate_constraint, substream, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, dice_channels_constraint, substream, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end; err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); end: return err; } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; unsigned int source, rate; bool internal; int err; err = snd_dice_stream_lock_try(dice); if (err < 0) goto end; err = init_hw_info(dice, substream); if (err < 0) goto err_locked; err = snd_dice_transaction_get_clock_source(dice, &source); if (err < 0) goto err_locked; switch (source) { case CLOCK_SOURCE_AES1: case CLOCK_SOURCE_AES2: case CLOCK_SOURCE_AES3: case CLOCK_SOURCE_AES4: case CLOCK_SOURCE_AES_ANY: case CLOCK_SOURCE_ADAT: case CLOCK_SOURCE_TDIF: case CLOCK_SOURCE_WC: internal = false; break; default: internal = true; break; } /* * When source of clock is not internal or any PCM streams are running, * available sampling rate is limited at current sampling rate. */ if (!internal || amdtp_stream_pcm_running(&dice->tx_stream) || amdtp_stream_pcm_running(&dice->rx_stream)) { err = snd_dice_transaction_get_rate(dice, &rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_dice_stream_lock_release(dice); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; snd_dice_stream_lock_release(dice); return 0; } static int capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dice->mutex); dice->substreams_counter++; mutex_unlock(&dice->mutex); } amdtp_stream_set_pcm_format(&dice->tx_stream, params_format(hw_params)); return snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); } static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dice->mutex); dice->substreams_counter++; mutex_unlock(&dice->mutex); } amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params)); return snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); } static int capture_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; mutex_lock(&dice->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dice->substreams_counter--; snd_dice_stream_stop_duplex(dice); mutex_unlock(&dice->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; mutex_lock(&dice->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dice->substreams_counter--; snd_dice_stream_stop_duplex(dice); mutex_unlock(&dice->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int capture_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; int err; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) amdtp_stream_pcm_prepare(&dice->tx_stream); return 0; } static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; int err; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) amdtp_stream_pcm_prepare(&dice->rx_stream); return err; } static int capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dice->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dice->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; return amdtp_stream_pcm_pointer(&dice->tx_stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; return amdtp_stream_pcm_pointer(&dice->rx_stream); } int snd_dice_create_pcm(struct snd_dice *dice) { static struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = capture_hw_params, .hw_free = capture_hw_free, .prepare = capture_prepare, .trigger = capture_trigger, .pointer = capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; static struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = playback_hw_params, .hw_free = playback_hw_free, .prepare = playback_prepare, .trigger = playback_trigger, .pointer = playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; unsigned int i, capture, playback; int err; capture = playback = 0; for (i = 0; i < 3; i++) { if (dice->tx_channels[i] > 0) capture = 1; if (dice->rx_channels[i] > 0) playback = 1; } err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); if (err < 0) return err; pcm->private_data = dice; strcpy(pcm->name, dice->card->shortname); if (capture > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); if (playback > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); return 0; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(c, formations[i].pcm)) continue; t.min = min(t.min, snd_bebob_rate_table[i]); t.max = max(t.max, snd_bebob_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(r, snd_bebob_rate_table[i])) continue; t.min = min(t.min, formations[i].pcm); t.max = max(t.max, formations[i].pcm); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; hw->rates = 0; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->channels_min = min(hw->channels_min, formations[i].pcm); hw->channels_max = max(hw->channels_max, formations[i].pcm); hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); } } static int pcm_init_hw_params(struct snd_bebob *bebob, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; struct snd_bebob_stream_formation *formations; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &bebob->tx_stream; formations = bebob->tx_stream_formations; } else { runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; s = &bebob->rx_stream; formations = bebob->rx_stream_formations; } limit_channels_and_rates(&runtime->hw, formations); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, formations, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, formations, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto end; err = amdtp_am824_add_pcm_hw_constraints(s, runtime); end: return err; } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; const struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; enum snd_bebob_clock_type src; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) goto end; err = pcm_init_hw_params(bebob, substream); if (err < 0) goto err_locked; err = snd_bebob_stream_get_clock_src(bebob, &src); if (err < 0) goto err_locked; /* * When source of clock is internal or any PCM stream are running, * the available sampling rate is limited at current sampling rate. */ if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { err = spec->get(bebob, &sampling_rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to get sampling rate: %d\n", err); goto err_locked; } substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_bebob_stream_lock_release(bebob); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_lock_release(bebob); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->tx_stream); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->rx_stream); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; return amdtp_stream_pcm_ack(&bebob->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; return amdtp_stream_pcm_ack(&bebob->rx_stream); } int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm); if (err < 0) goto end; pcm->private_data = bebob; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); end: return err; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule, struct snd_bebob *bebob, struct snd_bebob_stream_formation *formations) { struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(c, formations[i].pcm)) continue; t.min = min(t.min, snd_bebob_rate_table[i]); t.max = max(t.max, snd_bebob_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule, struct snd_bebob *bebob, struct snd_bebob_stream_formation *formations) { struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(r, snd_bebob_rate_table[i])) continue; t.min = min(t.min, formations[i].pcm); t.max = max(t.max, formations[i].pcm); } return snd_interval_refine(c, &t); } static inline int hw_rule_capture_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob *bebob = rule->private; return hw_rule_rate(params, rule, bebob, bebob->tx_stream_formations); } static inline int hw_rule_playback_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob *bebob = rule->private; return hw_rule_rate(params, rule, bebob, bebob->rx_stream_formations); } static inline int hw_rule_capture_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob *bebob = rule->private; return hw_rule_channels(params, rule, bebob, bebob->tx_stream_formations); } static inline int hw_rule_playback_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob *bebob = rule->private; return hw_rule_channels(params, rule, bebob, bebob->rx_stream_formations); } static void prepare_channels(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->channels_min = min(hw->channels_min, formations[i].pcm); hw->channels_max = max(hw->channels_max, formations[i].pcm); } return; } static void prepare_rates(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); } return; }
/* * parse the format descriptor and stores the possible sample rates * on the audioformat table (audio class v2). */ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip, struct audioformat *fp, struct usb_host_interface *iface) { struct usb_device *dev = chip->dev; unsigned char tmp[2], *data; int i, nr_rates, data_size, ret = 0; /* get the number of sample rates first by only fetching 2 bytes */ ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8, tmp, sizeof(tmp), 1000); if (ret < 0) { snd_printk(KERN_ERR "unable to retrieve number of sample rates\n"); goto err; } nr_rates = (tmp[1] << 8) | tmp[0]; data_size = 2 + 12 * nr_rates; data = kzalloc(data_size, GFP_KERNEL); if (!data) { ret = -ENOMEM; goto err; } /* now get the full information */ ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8, data, data_size, 1000); if (ret < 0) { snd_printk(KERN_ERR "unable to retrieve sample rate range\n"); ret = -EINVAL; goto err_free; } fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); if (!fp->rate_table) { ret = -ENOMEM; goto err_free; } fp->nr_rates = 0; fp->rate_min = fp->rate_max = 0; for (i = 0; i < nr_rates; i++) { int rate = combine_quad(&data[2 + 12 * i]); fp->rate_table[fp->nr_rates] = rate; if (!fp->rate_min || rate < fp->rate_min) fp->rate_min = rate; if (!fp->rate_max || rate > fp->rate_max) fp->rate_max = rate; fp->rates |= snd_pcm_rate_to_rate_bit(rate); fp->nr_rates++; } err_free: kfree(data); err: return ret; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(c, pcm_channels[mode])) continue; t.min = min(t.min, amdtp_rate_table[i]); t.max = max(t.max, amdtp_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(r, amdtp_rate_table[i])) continue; t.min = min(t.min, pcm_channels[mode]); t.max = max(t.max, pcm_channels[mode]); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, const unsigned int *pcm_channels) { unsigned int rate, channels; int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; channels = pcm_channels[mode]; if (pcm_channels[mode] == 0) continue; hw->channels_min = min(hw->channels_min, channels); hw->channels_max = max(hw->channels_max, channels); rate = amdtp_rate_table[i]; hw->rates |= snd_pcm_rate_to_rate_bit(rate); hw->rate_min = min(hw->rate_min, rate); hw->rate_max = max(hw->rate_max, rate); } } static int pcm_init_hw_params(struct snd_ff *ff, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; const unsigned int *pcm_channels; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->tx_stream; pcm_channels = ff->spec->pcm_capture_channels; } else { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->rx_stream; pcm_channels = ff->spec->pcm_playback_channels; } limit_channels_and_rates(&runtime->hw, pcm_channels); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; return amdtp_ff_add_pcm_hw_constraints(s, runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; unsigned int rate; enum snd_ff_clock_src src; int i, err; err = snd_ff_stream_lock_try(ff); if (err < 0) return err; err = pcm_init_hw_params(ff, substream); if (err < 0) goto release_lock; err = snd_ff_transaction_get_clock(ff, &rate, &src); if (err < 0) goto release_lock; if (src != SND_FF_CLOCK_SRC_INTERNAL) { for (i = 0; i < CIP_SFC_COUNT; ++i) { if (amdtp_rate_table[i] == rate) break; } /* * The unit is configured at sampling frequency which packet * streaming engine can't support. */ if (i >= CIP_SFC_COUNT) { err = -EIO; goto release_lock; } substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } else { if (amdtp_stream_pcm_running(&ff->rx_stream) || amdtp_stream_pcm_running(&ff->tx_stream)) { rate = amdtp_rate_table[ff->rx_stream.sfc]; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } } snd_pcm_set_sync(substream); return 0; release_lock: snd_ff_stream_lock_release(ff); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; snd_ff_stream_lock_release(ff); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->tx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->rx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->rx_stream); } int snd_ff_create_pcm_devices(struct snd_ff *ff) { static const struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = ff; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", ff->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); return 0; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(c, formations[i].pcm)) continue; t.min = min(t.min, snd_bebob_rate_table[i]); t.max = max(t.max, snd_bebob_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(r, snd_bebob_rate_table[i])) continue; t.min = min(t.min, formations[i].pcm); t.max = max(t.max, formations[i].pcm); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; hw->rates = 0; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->channels_min = min(hw->channels_min, formations[i].pcm); hw->channels_max = max(hw->channels_max, formations[i].pcm); hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); } } static void limit_period_and_buffer(struct snd_pcm_hardware *hw) { hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ hw->periods_max = UINT_MAX; hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */ /* Just to prevent from allocating much pages. */ hw->period_bytes_max = hw->period_bytes_min * 2048; hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; }