brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · 63457d2 Raw
402 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ff-pcm.c - a part of driver for RME Fireface series4 *5 * Copyright (c) 2015-2017 Takashi Sakamoto6 */7 8#include "ff.h"9 10static int hw_rule_rate(struct snd_pcm_hw_params *params,11			struct snd_pcm_hw_rule *rule)12{13	const unsigned int *pcm_channels = rule->private;14	struct snd_interval *r =15		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);16	const struct snd_interval *c =17		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);18	struct snd_interval t = {19		.min = UINT_MAX, .max = 0, .integer = 120	};21	unsigned int i;22 23	for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {24		enum snd_ff_stream_mode mode;25		int err;26 27		err = snd_ff_stream_get_multiplier_mode(i, &mode);28		if (err < 0)29			continue;30 31		if (!snd_interval_test(c, pcm_channels[mode]))32			continue;33 34		t.min = min(t.min, amdtp_rate_table[i]);35		t.max = max(t.max, amdtp_rate_table[i]);36	}37 38	return snd_interval_refine(r, &t);39}40 41static int hw_rule_channels(struct snd_pcm_hw_params *params,42			    struct snd_pcm_hw_rule *rule)43{44	const unsigned int *pcm_channels = rule->private;45	struct snd_interval *c =46		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);47	const struct snd_interval *r =48		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);49	struct snd_interval t = {50		.min = UINT_MAX, .max = 0, .integer = 151	};52	unsigned int i;53 54	for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {55		enum snd_ff_stream_mode mode;56		int err;57 58		err = snd_ff_stream_get_multiplier_mode(i, &mode);59		if (err < 0)60			continue;61 62		if (!snd_interval_test(r, amdtp_rate_table[i]))63			continue;64 65		t.min = min(t.min, pcm_channels[mode]);66		t.max = max(t.max, pcm_channels[mode]);67	}68 69	return snd_interval_refine(c, &t);70}71 72static void limit_channels_and_rates(struct snd_pcm_hardware *hw,73				     const unsigned int *pcm_channels)74{75	unsigned int rate, channels;76	int i;77 78	hw->channels_min = UINT_MAX;79	hw->channels_max = 0;80	hw->rate_min = UINT_MAX;81	hw->rate_max = 0;82 83	for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {84		enum snd_ff_stream_mode mode;85		int err;86 87		err = snd_ff_stream_get_multiplier_mode(i, &mode);88		if (err < 0)89			continue;90 91		channels = pcm_channels[mode];92		if (pcm_channels[mode] == 0)93			continue;94		hw->channels_min = min(hw->channels_min, channels);95		hw->channels_max = max(hw->channels_max, channels);96 97		rate = amdtp_rate_table[i];98		hw->rates |= snd_pcm_rate_to_rate_bit(rate);99		hw->rate_min = min(hw->rate_min, rate);100		hw->rate_max = max(hw->rate_max, rate);101	}102}103 104static int pcm_init_hw_params(struct snd_ff *ff,105			      struct snd_pcm_substream *substream)106{107	struct snd_pcm_runtime *runtime = substream->runtime;108	struct amdtp_stream *s;109	const unsigned int *pcm_channels;110	int err;111 112	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {113		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;114		s = &ff->tx_stream;115		pcm_channels = ff->spec->pcm_capture_channels;116	} else {117		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;118		s = &ff->rx_stream;119		pcm_channels = ff->spec->pcm_playback_channels;120	}121 122	limit_channels_and_rates(&runtime->hw, pcm_channels);123 124	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,125				  hw_rule_channels, (void *)pcm_channels,126				  SNDRV_PCM_HW_PARAM_RATE, -1);127	if (err < 0)128		return err;129 130	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,131				  hw_rule_rate, (void *)pcm_channels,132				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);133	if (err < 0)134		return err;135 136	return amdtp_ff_add_pcm_hw_constraints(s, runtime);137}138 139static int pcm_open(struct snd_pcm_substream *substream)140{141	struct snd_ff *ff = substream->private_data;142	struct amdtp_domain *d = &ff->domain;143	unsigned int rate;144	enum snd_ff_clock_src src;145	int i, err;146 147	err = snd_ff_stream_lock_try(ff);148	if (err < 0)149		return err;150 151	err = pcm_init_hw_params(ff, substream);152	if (err < 0)153		goto release_lock;154 155	err = ff->spec->protocol->get_clock(ff, &rate, &src);156	if (err < 0)157		goto release_lock;158 159	mutex_lock(&ff->mutex);160 161	// When source of clock is not internal or any stream is reserved for162	// transmission of PCM frames, the available sampling rate is limited163	// at current one.164	if (src != SND_FF_CLOCK_SRC_INTERNAL) {165		for (i = 0; i < CIP_SFC_COUNT; ++i) {166			if (amdtp_rate_table[i] == rate)167				break;168		}169 170		// The unit is configured at sampling frequency which packet171		// streaming engine can't support.172		if (i >= CIP_SFC_COUNT) {173			mutex_unlock(&ff->mutex);174			err = -EIO;175			goto release_lock;176		}177 178		substream->runtime->hw.rate_min = rate;179		substream->runtime->hw.rate_max = rate;180	} else {181		if (ff->substreams_counter > 0) {182			unsigned int frames_per_period = d->events_per_period;183			unsigned int frames_per_buffer = d->events_per_buffer;184 185			rate = amdtp_rate_table[ff->rx_stream.sfc];186			substream->runtime->hw.rate_min = rate;187			substream->runtime->hw.rate_max = rate;188 189			err = snd_pcm_hw_constraint_minmax(substream->runtime,190					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,191					frames_per_period, frames_per_period);192			if (err < 0) {193				mutex_unlock(&ff->mutex);194				goto release_lock;195			}196 197			err = snd_pcm_hw_constraint_minmax(substream->runtime,198					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,199					frames_per_buffer, frames_per_buffer);200			if (err < 0) {201				mutex_unlock(&ff->mutex);202				goto release_lock;203			}204		}205	}206 207	mutex_unlock(&ff->mutex);208 209	snd_pcm_set_sync(substream);210 211	return 0;212 213release_lock:214	snd_ff_stream_lock_release(ff);215	return err;216}217 218static int pcm_close(struct snd_pcm_substream *substream)219{220	struct snd_ff *ff = substream->private_data;221 222	snd_ff_stream_lock_release(ff);223 224	return 0;225}226 227static int pcm_hw_params(struct snd_pcm_substream *substream,228			 struct snd_pcm_hw_params *hw_params)229{230	struct snd_ff *ff = substream->private_data;231	int err = 0;232 233	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {234		unsigned int rate = params_rate(hw_params);235		unsigned int frames_per_period = params_period_size(hw_params);236		unsigned int frames_per_buffer = params_buffer_size(hw_params);237 238		mutex_lock(&ff->mutex);239		err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period,240						   frames_per_buffer);241		if (err >= 0)242			++ff->substreams_counter;243		mutex_unlock(&ff->mutex);244	}245 246	return err;247}248 249static int pcm_hw_free(struct snd_pcm_substream *substream)250{251	struct snd_ff *ff = substream->private_data;252 253	mutex_lock(&ff->mutex);254 255	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)256		--ff->substreams_counter;257 258	snd_ff_stream_stop_duplex(ff);259 260	mutex_unlock(&ff->mutex);261 262	return 0;263}264 265static int pcm_capture_prepare(struct snd_pcm_substream *substream)266{267	struct snd_ff *ff = substream->private_data;268	struct snd_pcm_runtime *runtime = substream->runtime;269	int err;270 271	mutex_lock(&ff->mutex);272 273	err = snd_ff_stream_start_duplex(ff, runtime->rate);274	if (err >= 0)275		amdtp_stream_pcm_prepare(&ff->tx_stream);276 277	mutex_unlock(&ff->mutex);278 279	return err;280}281 282static int pcm_playback_prepare(struct snd_pcm_substream *substream)283{284	struct snd_ff *ff = substream->private_data;285	struct snd_pcm_runtime *runtime = substream->runtime;286	int err;287 288	mutex_lock(&ff->mutex);289 290	err = snd_ff_stream_start_duplex(ff, runtime->rate);291	if (err >= 0)292		amdtp_stream_pcm_prepare(&ff->rx_stream);293 294	mutex_unlock(&ff->mutex);295 296	return err;297}298 299static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)300{301	struct snd_ff *ff = substream->private_data;302 303	switch (cmd) {304	case SNDRV_PCM_TRIGGER_START:305		amdtp_stream_pcm_trigger(&ff->tx_stream, substream);306		break;307	case SNDRV_PCM_TRIGGER_STOP:308		amdtp_stream_pcm_trigger(&ff->tx_stream, NULL);309		break;310	default:311		return -EINVAL;312	}313 314	return 0;315}316 317static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)318{319	struct snd_ff *ff = substream->private_data;320 321	switch (cmd) {322	case SNDRV_PCM_TRIGGER_START:323		amdtp_stream_pcm_trigger(&ff->rx_stream, substream);324		break;325	case SNDRV_PCM_TRIGGER_STOP:326		amdtp_stream_pcm_trigger(&ff->rx_stream, NULL);327		break;328	default:329		return -EINVAL;330	}331 332	return 0;333}334 335static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)336{337	struct snd_ff *ff = sbstrm->private_data;338 339	return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->tx_stream);340}341 342static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)343{344	struct snd_ff *ff = sbstrm->private_data;345 346	return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->rx_stream);347}348 349static int pcm_capture_ack(struct snd_pcm_substream *substream)350{351	struct snd_ff *ff = substream->private_data;352 353	return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->tx_stream);354}355 356static int pcm_playback_ack(struct snd_pcm_substream *substream)357{358	struct snd_ff *ff = substream->private_data;359 360	return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->rx_stream);361}362 363int snd_ff_create_pcm_devices(struct snd_ff *ff)364{365	static const struct snd_pcm_ops pcm_capture_ops = {366		.open		= pcm_open,367		.close		= pcm_close,368		.hw_params	= pcm_hw_params,369		.hw_free	= pcm_hw_free,370		.prepare	= pcm_capture_prepare,371		.trigger	= pcm_capture_trigger,372		.pointer	= pcm_capture_pointer,373		.ack		= pcm_capture_ack,374	};375	static const struct snd_pcm_ops pcm_playback_ops = {376		.open		= pcm_open,377		.close		= pcm_close,378		.hw_params	= pcm_hw_params,379		.hw_free	= pcm_hw_free,380		.prepare	= pcm_playback_prepare,381		.trigger	= pcm_playback_trigger,382		.pointer	= pcm_playback_pointer,383		.ack		= pcm_playback_ack,384	};385	struct snd_pcm *pcm;386	int err;387 388	err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm);389	if (err < 0)390		return err;391 392	pcm->private_data = ff;393	pcm->nonatomic = true;394	snprintf(pcm->name, sizeof(pcm->name),395		 "%s PCM", ff->card->shortname);396	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);397	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);398	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);399 400	return 0;401}402