Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4edeb831 authored by Clemens Ladisch's avatar Clemens Ladisch
Browse files

ALSA: dice: dynamic sample rate selection



Instead of relying of some control panel application to configure some
fixed sample rate, allow applications to set it automatically.

Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
parent 15a75c8b
Loading
Loading
Loading
Loading
+102 −35
Original line number Diff line number Diff line
@@ -70,6 +70,17 @@ static const unsigned int dice_rates[] = {
	[6] = 192000,
};

static unsigned int rate_to_index(unsigned int rate)
{
	unsigned int i;

	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
		if (dice_rates[i] == rate)
			return i;

	return 0;
}

static unsigned int rate_index_to_mode(unsigned int rate_index)
{
	return ((int)rate_index - 1) / 2;
@@ -302,6 +313,59 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
	wake_up(&dice->hwdep_wait);
}

static int dice_rate_constraint(struct snd_pcm_hw_params *params,
				struct snd_pcm_hw_rule *rule)
{
	struct dice *dice = rule->private;
	const struct snd_interval *channels =
		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
	struct snd_interval *rate =
		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
	struct snd_interval allowed_rates = {
		.min = UINT_MAX, .max = 0, .integer = 1
	};
	unsigned int i, mode;

	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) {
		mode = rate_index_to_mode(i);
		if ((dice->clock_caps & (1 << i)) &&
		    snd_interval_test(channels, dice->rx_channels[mode])) {
			allowed_rates.min = min(allowed_rates.min,
						dice_rates[i]);
			allowed_rates.max = max(allowed_rates.max,
						dice_rates[i]);
		}
	}

	return snd_interval_refine(rate, &allowed_rates);
}

static int dice_channels_constraint(struct snd_pcm_hw_params *params,
				    struct snd_pcm_hw_rule *rule)
{
	struct dice *dice = rule->private;
	const struct snd_interval *rate =
		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
	struct snd_interval *channels =
		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
	struct snd_interval allowed_channels = {
		.min = UINT_MAX, .max = 0, .integer = 1
	};
	unsigned int i, mode;

	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
		if ((dice->clock_caps & (1 << i)) &&
		    snd_interval_test(rate, dice_rates[i])) {
			mode = rate_index_to_mode(i);
			allowed_channels.min = min(allowed_channels.min,
						   dice->rx_channels[mode]);
			allowed_channels.max = max(allowed_channels.max,
						   dice->rx_channels[mode]);
		}

	return snd_interval_refine(channels, &allowed_channels);
}

static int dice_open(struct snd_pcm_substream *substream)
{
	static const struct snd_pcm_hardware hardware = {
@@ -311,6 +375,8 @@ static int dice_open(struct snd_pcm_substream *substream)
			SNDRV_PCM_INFO_INTERLEAVED |
			SNDRV_PCM_INFO_BLOCK_TRANSFER,
		.formats = AMDTP_OUT_PCM_FORMAT_BITS,
		.channels_min = UINT_MAX,
		.channels_max = 0,
		.buffer_bytes_max = 16 * 1024 * 1024,
		.period_bytes_min = 1,
		.period_bytes_max = UINT_MAX,
@@ -319,53 +385,46 @@ static int dice_open(struct snd_pcm_substream *substream)
	};
	struct dice *dice = substream->private_data;
	struct snd_pcm_runtime *runtime = substream->runtime;
	__be32 clock_sel, data[2];
	unsigned int rate_index, number_audio, number_midi;
	unsigned int i;
	int err;

	err = dice_try_lock(dice);
	if (err < 0)
		goto error;

	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
				 global_address(dice, GLOBAL_CLOCK_SELECT),
				 &clock_sel, 4, 0);
	if (err < 0)
		goto err_lock;
	rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK)
							>> CLOCK_RATE_SHIFT;
	if (rate_index >= ARRAY_SIZE(dice_rates)) {
		err = -ENXIO;
		goto err_lock;
	}

	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
				 rx_address(dice, RX_NUMBER_AUDIO),
				 data, 2 * 4, 0);
	if (err < 0)
		goto err_lock;
	number_audio = be32_to_cpu(data[0]);
	number_midi = be32_to_cpu(data[1]);

	runtime->hw = hardware;

	runtime->hw.rates = snd_pcm_rate_to_rate_bit(dice_rates[rate_index]);
	for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
		if (dice->clock_caps & (1 << i))
			runtime->hw.rates |=
				snd_pcm_rate_to_rate_bit(dice_rates[i]);
	snd_pcm_limit_hw_rates(runtime);

	runtime->hw.channels_min = number_audio;
	runtime->hw.channels_max = number_audio;
	for (i = 0; i < 3; ++i)
		if (dice->rx_channels[i]) {
			runtime->hw.channels_min = min(runtime->hw.channels_min,
						       dice->rx_channels[i]);
			runtime->hw.channels_max = max(runtime->hw.channels_max,
						       dice->rx_channels[i]);
		}

	amdtp_out_stream_set_parameters(&dice->stream, dice_rates[rate_index],
					number_audio, number_midi);
	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
				  dice_rate_constraint, dice,
				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
	if (err < 0)
		goto err_lock;
	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
				  dice_channels_constraint, dice,
				  SNDRV_PCM_HW_PARAM_RATE, -1);
	if (err < 0)
		goto err_lock;

	err = snd_pcm_hw_constraint_step(runtime, 0,
					 SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
					 amdtp_syt_intervals[rate_index]);
					 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
	if (err < 0)
		goto err_lock;
	err = snd_pcm_hw_constraint_step(runtime, 0,
					 SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
					 amdtp_syt_intervals[rate_index]);
					 SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
	if (err < 0)
		goto err_lock;

@@ -502,6 +561,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
			  struct snd_pcm_hw_params *hw_params)
{
	struct dice *dice = substream->private_data;
	unsigned int rate_index, mode;
	int err;

	mutex_lock(&dice->mutex);
@@ -511,15 +571,22 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
					       params_buffer_bytes(hw_params));
	if (err < 0)
		goto error;
		return err;

	rate_index = rate_to_index(params_rate(hw_params));
	err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
	if (err < 0)
		return err;

	mode = rate_index_to_mode(rate_index);
	amdtp_out_stream_set_parameters(&dice->stream,
					params_rate(hw_params),
					params_channels(hw_params),
					dice->rx_midi_ports[mode]);
	amdtp_out_stream_set_pcm_format(&dice->stream,
					params_format(hw_params));

	return 0;

error:
	return err;
}

static int dice_hw_free(struct snd_pcm_substream *substream)