brintos

brintos / linux-shallow public Read only

0
0
Text · 9.3 KiB · f3b4849 Raw
372 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * motu-pcm.c - a part of driver for MOTU FireWire series4 *5 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>6 */7 8#include <sound/pcm_params.h>9#include "motu.h"10 11static int motu_rate_constraint(struct snd_pcm_hw_params *params,12				struct snd_pcm_hw_rule *rule)13{14	struct snd_motu_packet_format *formats = rule->private;15 16	const struct snd_interval *c =17		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);18	struct snd_interval *r =19		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);20	struct snd_interval rates = {21		.min = UINT_MAX, .max = 0, .integer = 122	};23	unsigned int i, pcm_channels, rate, mode;24 25	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {26		rate = snd_motu_clock_rates[i];27		mode = i / 2;28 29		pcm_channels = formats->pcm_chunks[mode];30		if (!snd_interval_test(c, pcm_channels))31			continue;32 33		rates.min = min(rates.min, rate);34		rates.max = max(rates.max, rate);35	}36 37	return snd_interval_refine(r, &rates);38}39 40static int motu_channels_constraint(struct snd_pcm_hw_params *params,41				    struct snd_pcm_hw_rule *rule)42{43	struct snd_motu_packet_format *formats = rule->private;44 45	const struct snd_interval *r =46		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);47	struct snd_interval *c =48		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);49	struct snd_interval channels = {50		.min = UINT_MAX, .max = 0, .integer = 151	};52	unsigned int i, pcm_channels, rate, mode;53 54	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {55		rate = snd_motu_clock_rates[i];56		mode = i / 2;57 58		if (!snd_interval_test(r, rate))59			continue;60 61		pcm_channels = formats->pcm_chunks[mode];62		channels.min = min(channels.min, pcm_channels);63		channels.max = max(channels.max, pcm_channels);64	}65 66	return snd_interval_refine(c, &channels);67}68 69static void limit_channels_and_rates(struct snd_motu *motu,70				     struct snd_pcm_runtime *runtime,71				     struct snd_motu_packet_format *formats)72{73	struct snd_pcm_hardware *hw = &runtime->hw;74	unsigned int i, pcm_channels, rate, mode;75 76	hw->channels_min = UINT_MAX;77	hw->channels_max = 0;78 79	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {80		rate = snd_motu_clock_rates[i];81		mode = i / 2;82 83		pcm_channels = formats->pcm_chunks[mode];84		if (pcm_channels == 0)85			continue;86 87		hw->rates |= snd_pcm_rate_to_rate_bit(rate);88		hw->channels_min = min(hw->channels_min, pcm_channels);89		hw->channels_max = max(hw->channels_max, pcm_channels);90	}91 92	snd_pcm_limit_hw_rates(runtime);93}94 95static int init_hw_info(struct snd_motu *motu,96			struct snd_pcm_substream *substream)97{98	struct snd_pcm_runtime *runtime = substream->runtime;99	struct snd_pcm_hardware *hw = &runtime->hw;100	struct amdtp_stream *stream;101	struct snd_motu_packet_format *formats;102	int err;103 104	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {105		hw->formats = SNDRV_PCM_FMTBIT_S32;106		stream = &motu->tx_stream;107		formats = &motu->tx_packet_formats;108	} else {109		hw->formats = SNDRV_PCM_FMTBIT_S32;110		stream = &motu->rx_stream;111		formats = &motu->rx_packet_formats;112	}113 114	limit_channels_and_rates(motu, runtime, formats);115 116	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,117				  motu_rate_constraint, formats,118				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);119	if (err < 0)120		return err;121	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,122				  motu_channels_constraint, formats,123				  SNDRV_PCM_HW_PARAM_RATE, -1);124	if (err < 0)125		return err;126 127	return amdtp_motu_add_pcm_hw_constraints(stream, runtime);128}129 130static int pcm_open(struct snd_pcm_substream *substream)131{132	struct snd_motu *motu = substream->private_data;133	struct amdtp_domain *d = &motu->domain;134	enum snd_motu_clock_source src;135	int err;136 137	err = snd_motu_stream_lock_try(motu);138	if (err < 0)139		return err;140 141	mutex_lock(&motu->mutex);142 143	err = snd_motu_stream_cache_packet_formats(motu);144	if (err < 0)145		goto err_locked;146 147	err = init_hw_info(motu, substream);148	if (err < 0)149		goto err_locked;150 151	err = snd_motu_protocol_get_clock_source(motu, &src);152	if (err < 0)153		goto err_locked;154 155	// When source of clock is not internal or any stream is reserved for156	// transmission of PCM frames, the available sampling rate is limited157	// at current one.158	if ((src != SND_MOTU_CLOCK_SOURCE_INTERNAL &&159	     src != SND_MOTU_CLOCK_SOURCE_SPH) ||160	    (motu->substreams_counter > 0 && d->events_per_period > 0)) {161		unsigned int frames_per_period = d->events_per_period;162		unsigned int frames_per_buffer = d->events_per_buffer;163		unsigned int rate;164 165		err = snd_motu_protocol_get_clock_rate(motu, &rate);166		if (err < 0)167			goto err_locked;168 169		substream->runtime->hw.rate_min = rate;170		substream->runtime->hw.rate_max = rate;171 172		if (frames_per_period > 0) {173			err = snd_pcm_hw_constraint_minmax(substream->runtime,174					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,175					frames_per_period, frames_per_period);176			if (err < 0)177				goto err_locked;178 179			err = snd_pcm_hw_constraint_minmax(substream->runtime,180					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,181					frames_per_buffer, frames_per_buffer);182			if (err < 0)183				goto err_locked;184		}185	}186 187	snd_pcm_set_sync(substream);188 189	mutex_unlock(&motu->mutex);190 191	return 0;192err_locked:193	mutex_unlock(&motu->mutex);194	snd_motu_stream_lock_release(motu);195	return err;196}197 198static int pcm_close(struct snd_pcm_substream *substream)199{200	struct snd_motu *motu = substream->private_data;201 202	snd_motu_stream_lock_release(motu);203 204	return 0;205}206 207static int pcm_hw_params(struct snd_pcm_substream *substream,208			 struct snd_pcm_hw_params *hw_params)209{210	struct snd_motu *motu = substream->private_data;211	int err = 0;212 213	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {214		unsigned int rate = params_rate(hw_params);215		unsigned int frames_per_period = params_period_size(hw_params);216		unsigned int frames_per_buffer = params_buffer_size(hw_params);217 218		mutex_lock(&motu->mutex);219		err = snd_motu_stream_reserve_duplex(motu, rate,220					frames_per_period, frames_per_buffer);221		if (err >= 0)222			++motu->substreams_counter;223		mutex_unlock(&motu->mutex);224	}225 226	return err;227}228 229static int pcm_hw_free(struct snd_pcm_substream *substream)230{231	struct snd_motu *motu = substream->private_data;232 233	mutex_lock(&motu->mutex);234 235	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)236		--motu->substreams_counter;237 238	snd_motu_stream_stop_duplex(motu);239 240	mutex_unlock(&motu->mutex);241 242	return 0;243}244 245static int capture_prepare(struct snd_pcm_substream *substream)246{247	struct snd_motu *motu = substream->private_data;248	int err;249 250	mutex_lock(&motu->mutex);251	err = snd_motu_stream_start_duplex(motu);252	mutex_unlock(&motu->mutex);253	if (err >= 0)254		amdtp_stream_pcm_prepare(&motu->tx_stream);255 256	return 0;257}258static int playback_prepare(struct snd_pcm_substream *substream)259{260	struct snd_motu *motu = substream->private_data;261	int err;262 263	mutex_lock(&motu->mutex);264	err = snd_motu_stream_start_duplex(motu);265	mutex_unlock(&motu->mutex);266	if (err >= 0)267		amdtp_stream_pcm_prepare(&motu->rx_stream);268 269	return err;270}271 272static int capture_trigger(struct snd_pcm_substream *substream, int cmd)273{274	struct snd_motu *motu = substream->private_data;275 276	switch (cmd) {277	case SNDRV_PCM_TRIGGER_START:278		amdtp_stream_pcm_trigger(&motu->tx_stream, substream);279		break;280	case SNDRV_PCM_TRIGGER_STOP:281		amdtp_stream_pcm_trigger(&motu->tx_stream, NULL);282		break;283	default:284		return -EINVAL;285	}286 287	return 0;288}289static int playback_trigger(struct snd_pcm_substream *substream, int cmd)290{291	struct snd_motu *motu = substream->private_data;292 293	switch (cmd) {294	case SNDRV_PCM_TRIGGER_START:295		amdtp_stream_pcm_trigger(&motu->rx_stream, substream);296		break;297	case SNDRV_PCM_TRIGGER_STOP:298		amdtp_stream_pcm_trigger(&motu->rx_stream, NULL);299		break;300	default:301		return -EINVAL;302	}303 304	return 0;305}306 307static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)308{309	struct snd_motu *motu = substream->private_data;310 311	return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->tx_stream);312}313static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)314{315	struct snd_motu *motu = substream->private_data;316 317	return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->rx_stream);318}319 320static int capture_ack(struct snd_pcm_substream *substream)321{322	struct snd_motu *motu = substream->private_data;323 324	return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->tx_stream);325}326 327static int playback_ack(struct snd_pcm_substream *substream)328{329	struct snd_motu *motu = substream->private_data;330 331	return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream);332}333 334int snd_motu_create_pcm_devices(struct snd_motu *motu)335{336	static const struct snd_pcm_ops capture_ops = {337		.open      = pcm_open,338		.close     = pcm_close,339		.hw_params = pcm_hw_params,340		.hw_free   = pcm_hw_free,341		.prepare   = capture_prepare,342		.trigger   = capture_trigger,343		.pointer   = capture_pointer,344		.ack       = capture_ack,345	};346	static const struct snd_pcm_ops playback_ops = {347		.open      = pcm_open,348		.close     = pcm_close,349		.hw_params = pcm_hw_params,350		.hw_free   = pcm_hw_free,351		.prepare   = playback_prepare,352		.trigger   = playback_trigger,353		.pointer   = playback_pointer,354		.ack       = playback_ack,355	};356	struct snd_pcm *pcm;357	int err;358 359	err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm);360	if (err < 0)361		return err;362	pcm->private_data = motu;363	pcm->nonatomic = true;364	strcpy(pcm->name, motu->card->shortname);365 366	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);367	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);368	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);369 370	return 0;371}372