brintos

brintos / linux-shallow public Read only

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