brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · a73003a Raw
291 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tascam-pcm.c - a part of driver for TASCAM FireWire series4 *5 * Copyright (c) 2015 Takashi Sakamoto6 */7 8#include "tascam.h"9 10static int pcm_init_hw_params(struct snd_tscm *tscm,11			      struct snd_pcm_substream *substream)12{13	struct snd_pcm_runtime *runtime = substream->runtime;14	struct snd_pcm_hardware *hw = &runtime->hw;15	struct amdtp_stream *stream;16	unsigned int pcm_channels;17 18	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {19		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;20		stream = &tscm->tx_stream;21		pcm_channels = tscm->spec->pcm_capture_analog_channels;22	} else {23		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;24		stream = &tscm->rx_stream;25		pcm_channels = tscm->spec->pcm_playback_analog_channels;26	}27 28	if (tscm->spec->has_adat)29		pcm_channels += 8;30	if (tscm->spec->has_spdif)31		pcm_channels += 2;32	runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;33 34	hw->rates = SNDRV_PCM_RATE_44100 |35		    SNDRV_PCM_RATE_48000 |36		    SNDRV_PCM_RATE_88200 |37		    SNDRV_PCM_RATE_96000;38	snd_pcm_limit_hw_rates(runtime);39 40	return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);41}42 43static int pcm_open(struct snd_pcm_substream *substream)44{45	struct snd_tscm *tscm = substream->private_data;46	struct amdtp_domain *d = &tscm->domain;47	enum snd_tscm_clock clock;48	int err;49 50	err = snd_tscm_stream_lock_try(tscm);51	if (err < 0)52		return err;53 54	err = pcm_init_hw_params(tscm, substream);55	if (err < 0)56		goto err_locked;57 58	err = snd_tscm_stream_get_clock(tscm, &clock);59	if (err < 0)60		goto err_locked;61 62	mutex_lock(&tscm->mutex);63 64	// When source of clock is not internal or any stream is reserved for65	// transmission of PCM frames, the available sampling rate is limited66	// at current one.67	if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {68		unsigned int frames_per_period = d->events_per_period;69		unsigned int frames_per_buffer = d->events_per_buffer;70		unsigned int rate;71 72		err = snd_tscm_stream_get_rate(tscm, &rate);73		if (err < 0) {74			mutex_unlock(&tscm->mutex);75			goto err_locked;76		}77		substream->runtime->hw.rate_min = rate;78		substream->runtime->hw.rate_max = rate;79 80		err = snd_pcm_hw_constraint_minmax(substream->runtime,81					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,82					frames_per_period, frames_per_period);83		if (err < 0) {84			mutex_unlock(&tscm->mutex);85			goto err_locked;86		}87 88		err = snd_pcm_hw_constraint_minmax(substream->runtime,89					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,90					frames_per_buffer, frames_per_buffer);91		if (err < 0) {92			mutex_unlock(&tscm->mutex);93			goto err_locked;94		}95	}96 97	mutex_unlock(&tscm->mutex);98 99	snd_pcm_set_sync(substream);100 101	return 0;102err_locked:103	snd_tscm_stream_lock_release(tscm);104	return err;105}106 107static int pcm_close(struct snd_pcm_substream *substream)108{109	struct snd_tscm *tscm = substream->private_data;110 111	snd_tscm_stream_lock_release(tscm);112 113	return 0;114}115 116static int pcm_hw_params(struct snd_pcm_substream *substream,117			 struct snd_pcm_hw_params *hw_params)118{119	struct snd_tscm *tscm = substream->private_data;120	int err = 0;121 122	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {123		unsigned int rate = params_rate(hw_params);124		unsigned int frames_per_period = params_period_size(hw_params);125		unsigned int frames_per_buffer = params_buffer_size(hw_params);126 127		mutex_lock(&tscm->mutex);128		err = snd_tscm_stream_reserve_duplex(tscm, rate,129					frames_per_period, frames_per_buffer);130		if (err >= 0)131			++tscm->substreams_counter;132		mutex_unlock(&tscm->mutex);133	}134 135	return err;136}137 138static int pcm_hw_free(struct snd_pcm_substream *substream)139{140	struct snd_tscm *tscm = substream->private_data;141 142	mutex_lock(&tscm->mutex);143 144	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)145		--tscm->substreams_counter;146 147	snd_tscm_stream_stop_duplex(tscm);148 149	mutex_unlock(&tscm->mutex);150 151	return 0;152}153 154static int pcm_capture_prepare(struct snd_pcm_substream *substream)155{156	struct snd_tscm *tscm = substream->private_data;157	struct snd_pcm_runtime *runtime = substream->runtime;158	int err;159 160	mutex_lock(&tscm->mutex);161 162	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);163	if (err >= 0)164		amdtp_stream_pcm_prepare(&tscm->tx_stream);165 166	mutex_unlock(&tscm->mutex);167 168	return err;169}170 171static int pcm_playback_prepare(struct snd_pcm_substream *substream)172{173	struct snd_tscm *tscm = substream->private_data;174	struct snd_pcm_runtime *runtime = substream->runtime;175	int err;176 177	mutex_lock(&tscm->mutex);178 179	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);180	if (err >= 0)181		amdtp_stream_pcm_prepare(&tscm->rx_stream);182 183	mutex_unlock(&tscm->mutex);184 185	return err;186}187 188static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)189{190	struct snd_tscm *tscm = substream->private_data;191 192	switch (cmd) {193	case SNDRV_PCM_TRIGGER_START:194		amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);195		break;196	case SNDRV_PCM_TRIGGER_STOP:197		amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);198		break;199	default:200		return -EINVAL;201	}202 203	return 0;204}205 206static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)207{208	struct snd_tscm *tscm = substream->private_data;209 210	switch (cmd) {211	case SNDRV_PCM_TRIGGER_START:212		amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);213		break;214	case SNDRV_PCM_TRIGGER_STOP:215		amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);216		break;217	default:218		return -EINVAL;219	}220 221	return 0;222}223 224static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)225{226	struct snd_tscm *tscm = sbstrm->private_data;227 228	return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);229}230 231static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)232{233	struct snd_tscm *tscm = sbstrm->private_data;234 235	return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);236}237 238static int pcm_capture_ack(struct snd_pcm_substream *substream)239{240	struct snd_tscm *tscm = substream->private_data;241 242	return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);243}244 245static int pcm_playback_ack(struct snd_pcm_substream *substream)246{247	struct snd_tscm *tscm = substream->private_data;248 249	return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);250}251 252int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)253{254	static const struct snd_pcm_ops capture_ops = {255		.open		= pcm_open,256		.close		= pcm_close,257		.hw_params	= pcm_hw_params,258		.hw_free	= pcm_hw_free,259		.prepare	= pcm_capture_prepare,260		.trigger	= pcm_capture_trigger,261		.pointer	= pcm_capture_pointer,262		.ack		= pcm_capture_ack,263	};264	static const struct snd_pcm_ops playback_ops = {265		.open		= pcm_open,266		.close		= pcm_close,267		.hw_params	= pcm_hw_params,268		.hw_free	= pcm_hw_free,269		.prepare	= pcm_playback_prepare,270		.trigger	= pcm_playback_trigger,271		.pointer	= pcm_playback_pointer,272		.ack		= pcm_playback_ack,273	};274	struct snd_pcm *pcm;275	int err;276 277	err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);278	if (err < 0)279		return err;280 281	pcm->private_data = tscm;282	pcm->nonatomic = true;283	snprintf(pcm->name, sizeof(pcm->name),284		 "%s PCM", tscm->card->shortname);285	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);286	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);287	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);288 289	return 0;290}291