예제 #1
0
파일: ua101.c 프로젝트: 454053205/linux
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;
}
예제 #2
0
/*
 * 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;
}
예제 #4
0
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;
}
예제 #5
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;
}
예제 #6
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;
}
예제 #7
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,
				&reg, sizeof(reg));
	} else {
		stream = &dice->rx_stream[index];
		err = snd_dice_transaction_read_rx(dice,
				size * index + RX_NUMBER_AUDIO,
				&reg, 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;
}
예제 #8
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;
}
예제 #9
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;
}
예제 #10
0
파일: dice-pcm.c 프로젝트: 19Dan01/linux
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;
}
예제 #11
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;
}
예제 #12
0
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;
}
예제 #13
0
/*
 * 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;
}
예제 #14
0
파일: ff-pcm.c 프로젝트: AlexShiLucky/linux
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;
}
예제 #15
0
파일: bebob_pcm.c 프로젝트: DenisLug/mptcp
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;
}