File Index
Files and directories
Sign in
FILE

dice-pcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/firewire/dice/dice-pcm.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * dice_pcm.c - a part of driver for DICE based devices
  4 *
  5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  6 * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
  7 */
  8
  9#include "dice.h"
 10
 11static int dice_rate_constraint(struct snd_pcm_hw_params *params,
 12				struct snd_pcm_hw_rule *rule)
 13{
 14	struct snd_pcm_substream *substream = rule->private;
 15	struct snd_dice *dice = substream->private_data;
 16	unsigned int index = substream->pcm->device;
 17
 18	const struct snd_interval *c =
 19		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
 20	struct snd_interval *r =
 21		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
 22	struct snd_interval rates = {
 23		.min = UINT_MAX, .max = 0, .integer = 1
 24	};
 25	unsigned int *pcm_channels;
 26	enum snd_dice_rate_mode mode;
 27	unsigned int i, rate;
 28
 29	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 30		pcm_channels = dice->tx_pcm_chs[index];
 31	else
 32		pcm_channels = dice->rx_pcm_chs[index];
 33
 34	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
 35		rate = snd_dice_rates[i];
 36		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
 37			continue;
 38
 39		if (!snd_interval_test(c, pcm_channels[mode]))
 40			continue;
 41
 42		rates.min = min(rates.min, rate);
 43		rates.max = max(rates.max, rate);
 44	}
 45
 46	return snd_interval_refine(r, &rates);
 47}
 48
 49static int dice_channels_constraint(struct snd_pcm_hw_params *params,
 50				    struct snd_pcm_hw_rule *rule)
 51{
 52	struct snd_pcm_substream *substream = rule->private;
 53	struct snd_dice *dice = substream->private_data;
 54	unsigned int index = substream->pcm->device;
 55
 56	const struct snd_interval *r =
 57		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
 58	struct snd_interval *c =
 59		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
 60	struct snd_interval channels = {
 61		.min = UINT_MAX, .max = 0, .integer = 1
 62	};
 63	unsigned int *pcm_channels;
 64	enum snd_dice_rate_mode mode;
 65	unsigned int i, rate;
 66
 67	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 68		pcm_channels = dice->tx_pcm_chs[index];
 69	else
 70		pcm_channels = dice->rx_pcm_chs[index];
 71
 72	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
 73		rate = snd_dice_rates[i];
 74		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
 75			continue;
 76
 77		if (!snd_interval_test(r, rate))
 78			continue;
 79
 80		channels.min = min(channels.min, pcm_channels[mode]);
 81		channels.max = max(channels.max, pcm_channels[mode]);
 82	}
 83
 84	return snd_interval_refine(c, &channels);
 85}
 86
 87static int limit_channels_and_rates(struct snd_dice *dice,
 88				    struct snd_pcm_runtime *runtime,
 89				    enum amdtp_stream_direction dir,
 90				    unsigned int index)
 91{
 92	struct snd_pcm_hardware *hw = &runtime->hw;
 93	unsigned int *pcm_channels;
 94	unsigned int i;
 95
 96	if (dir == AMDTP_IN_STREAM)
 97		pcm_channels = dice->tx_pcm_chs[index];
 98	else
 99		pcm_channels = dice->rx_pcm_chs[index];
100
101	hw->channels_min = UINT_MAX;
102	hw->channels_max = 0;
103
104	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
105		enum snd_dice_rate_mode mode;
106		unsigned int rate, channels;
107
108		rate = snd_dice_rates[i];
109		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
110			continue;
111		hw->rates |= snd_pcm_rate_to_rate_bit(rate);
112
113		channels = pcm_channels[mode];
114		if (channels == 0)
115			continue;
116		hw->channels_min = min(hw->channels_min, channels);
117		hw->channels_max = max(hw->channels_max, channels);
118	}
119
120	snd_pcm_limit_hw_rates(runtime);
121
122	return 0;
123}
124
125static int init_hw_info(struct snd_dice *dice,
126			struct snd_pcm_substream *substream)
127{
128	struct snd_pcm_runtime *runtime = substream->runtime;
129	struct snd_pcm_hardware *hw = &runtime->hw;
130	unsigned int index = substream->pcm->device;
131	enum amdtp_stream_direction dir;
132	struct amdtp_stream *stream;
133	int err;
134
135	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
136		hw->formats = AM824_IN_PCM_FORMAT_BITS;
137		dir = AMDTP_IN_STREAM;
138		stream = &dice->tx_stream[index];
139	} else {
140		hw->formats = AM824_OUT_PCM_FORMAT_BITS;
141		dir = AMDTP_OUT_STREAM;
142		stream = &dice->rx_stream[index];
143	}
144
145	err = limit_channels_and_rates(dice, substream->runtime, dir,
146				       index);
147	if (err < 0)
148		return err;
149
150	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
151				  dice_rate_constraint, substream,
152				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
153	if (err < 0)
154		return err;
155	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
156				  dice_channels_constraint, substream,
157				  SNDRV_PCM_HW_PARAM_RATE, -1);
158	if (err < 0)
159		return err;
160
161	return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
162}
163
164static int pcm_open(struct snd_pcm_substream *substream)
165{
166	struct snd_dice *dice = substream->private_data;
167	struct amdtp_domain *d = &dice->domain;
168	unsigned int source;
169	bool internal;
170	int err;
171
172	err = snd_dice_stream_lock_try(dice);
173	if (err < 0)
174		return err;
175
176	err = init_hw_info(dice, substream);
177	if (err < 0)
178		goto err_locked;
179
180	err = snd_dice_transaction_get_clock_source(dice, &source);
181	if (err < 0)
182		goto err_locked;
183	switch (source) {
184	case CLOCK_SOURCE_AES1:
185	case CLOCK_SOURCE_AES2:
186	case CLOCK_SOURCE_AES3:
187	case CLOCK_SOURCE_AES4:
188	case CLOCK_SOURCE_AES_ANY:
189	case CLOCK_SOURCE_ADAT:
190	case CLOCK_SOURCE_TDIF:
191	case CLOCK_SOURCE_WC:
192		internal = false;
193		break;
194	default:
195		internal = true;
196		break;
197	}
198
199	scoped_guard(mutex, &dice->mutex) {
200		// When source of clock is not internal or any stream is reserved for
201		// transmission of PCM frames, the available sampling rate is limited
202		// at current one.
203		if (!internal ||
204		    (dice->substreams_counter > 0 && d->events_per_period > 0)) {
205			unsigned int frames_per_period = d->events_per_period;
206			unsigned int frames_per_buffer = d->events_per_buffer;
207			unsigned int rate;
208
209			err = snd_dice_transaction_get_rate(dice, &rate);
210			if (err < 0)
211				goto err_locked;
212
213			substream->runtime->hw.rate_min = rate;
214			substream->runtime->hw.rate_max = rate;
215
216			if (frames_per_period > 0) {
217				// For double_pcm_frame quirk.
218				if (rate > 96000 && !dice->disable_double_pcm_frames) {
219					frames_per_period *= 2;
220					frames_per_buffer *= 2;
221				}
222
223				err = snd_pcm_hw_constraint_minmax(substream->runtime,
224								   SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
225								   frames_per_period, frames_per_period);
226				if (err < 0)
227					goto err_locked;
228
229				err = snd_pcm_hw_constraint_minmax(substream->runtime,
230								   SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
231								   frames_per_buffer, frames_per_buffer);
232				if (err < 0)
233					goto err_locked;
234			}
235		}
236	}
237
238	snd_pcm_set_sync(substream);
239
240	return 0;
241err_locked:
242	snd_dice_stream_lock_release(dice);
243	return err;
244}
245
246static int pcm_close(struct snd_pcm_substream *substream)
247{
248	struct snd_dice *dice = substream->private_data;
249
250	snd_dice_stream_lock_release(dice);
251
252	return 0;
253}
254
255static int pcm_hw_params(struct snd_pcm_substream *substream,
256			 struct snd_pcm_hw_params *hw_params)
257{
258	struct snd_dice *dice = substream->private_data;
259	int err = 0;
260
261	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
262		unsigned int rate = params_rate(hw_params);
263		unsigned int events_per_period = params_period_size(hw_params);
264		unsigned int events_per_buffer = params_buffer_size(hw_params);
265
266		guard(mutex)(&dice->mutex);
267		// For double_pcm_frame quirk.
268		if (rate > 96000 && !dice->disable_double_pcm_frames) {
269			events_per_period /= 2;
270			events_per_buffer /= 2;
271		}
272		err = snd_dice_stream_reserve_duplex(dice, rate,
273					events_per_period, events_per_buffer);
274		if (err >= 0)
275			++dice->substreams_counter;
276	}
277
278	return err;
279}
280
281static int pcm_hw_free(struct snd_pcm_substream *substream)
282{
283	struct snd_dice *dice = substream->private_data;
284
285	guard(mutex)(&dice->mutex);
286
287	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
288		--dice->substreams_counter;
289
290	snd_dice_stream_stop_duplex(dice);
291
292	return 0;
293}
294
295static int capture_prepare(struct snd_pcm_substream *substream)
296{
297	struct snd_dice *dice = substream->private_data;
298	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
299	int err;
300
301	scoped_guard(mutex, &dice->mutex) {
302		err = snd_dice_stream_start_duplex(dice);
303	}
304	if (err >= 0)
305		amdtp_stream_pcm_prepare(stream);
306
307	return 0;
308}
309static int playback_prepare(struct snd_pcm_substream *substream)
310{
311	struct snd_dice *dice = substream->private_data;
312	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
313	int err;
314
315	scoped_guard(mutex, &dice->mutex) {
316		err = snd_dice_stream_start_duplex(dice);
317	}
318	if (err >= 0)
319		amdtp_stream_pcm_prepare(stream);
320
321	return err;
322}
323
324static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
325{
326	struct snd_dice *dice = substream->private_data;
327	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
328
329	switch (cmd) {
330	case SNDRV_PCM_TRIGGER_START:
331		amdtp_stream_pcm_trigger(stream, substream);
332		break;
333	case SNDRV_PCM_TRIGGER_STOP:
334		amdtp_stream_pcm_trigger(stream, NULL);
335		break;
336	default:
337		return -EINVAL;
338	}
339
340	return 0;
341}
342static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
343{
344	struct snd_dice *dice = substream->private_data;
345	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
346
347	switch (cmd) {
348	case SNDRV_PCM_TRIGGER_START:
349		amdtp_stream_pcm_trigger(stream, substream);
350		break;
351	case SNDRV_PCM_TRIGGER_STOP:
352		amdtp_stream_pcm_trigger(stream, NULL);
353		break;
354	default:
355		return -EINVAL;
356	}
357
358	return 0;
359}
360
361static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
362{
363	struct snd_dice *dice = substream->private_data;
364	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
365
366	return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
367}
368static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
369{
370	struct snd_dice *dice = substream->private_data;
371	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
372
373	return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
374}
375
376static int capture_ack(struct snd_pcm_substream *substream)
377{
378	struct snd_dice *dice = substream->private_data;
379	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
380
381	return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
382}
383
384static int playback_ack(struct snd_pcm_substream *substream)
385{
386	struct snd_dice *dice = substream->private_data;
387	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
388
389	return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
390}
391
392int snd_dice_create_pcm(struct snd_dice *dice)
393{
394	static const struct snd_pcm_ops capture_ops = {
395		.open      = pcm_open,
396		.close     = pcm_close,
397		.hw_params = pcm_hw_params,
398		.hw_free   = pcm_hw_free,
399		.prepare   = capture_prepare,
400		.trigger   = capture_trigger,
401		.pointer   = capture_pointer,
402		.ack       = capture_ack,
403	};
404	static const struct snd_pcm_ops playback_ops = {
405		.open      = pcm_open,
406		.close     = pcm_close,
407		.hw_params = pcm_hw_params,
408		.hw_free   = pcm_hw_free,
409		.prepare   = playback_prepare,
410		.trigger   = playback_trigger,
411		.pointer   = playback_pointer,
412		.ack       = playback_ack,
413	};
414	struct snd_pcm *pcm;
415	unsigned int capture, playback;
416	int i, j;
417	int err;
418
419	for (i = 0; i < MAX_STREAMS; i++) {
420		capture = playback = 0;
421		for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {
422			if (dice->tx_pcm_chs[i][j] > 0)
423				capture = 1;
424			if (dice->rx_pcm_chs[i][j] > 0)
425				playback = 1;
426		}
427
428		err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
429				  &pcm);
430		if (err < 0)
431			return err;
432		pcm->private_data = dice;
433		pcm->nonatomic = true;
434		strscpy(pcm->name, dice->card->shortname);
435
436		if (capture > 0)
437			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
438					&capture_ops);
439
440		if (playback > 0)
441			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
442					&playback_ops);
443
444		snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
445					       NULL, 0, 0);
446	}
447
448	return 0;
449}
~/ Read-only file preview.