File Index
Files and directories
Sign in
FILE

oxfw-pcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/firewire/oxfw/oxfw-pcm.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * oxfw_pcm.c - a part of driver for OXFW970/971 based devices
  4 *
  5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  6 */
  7
  8#include "oxfw.h"
  9
 10static int hw_rule_rate(struct snd_pcm_hw_params *params,
 11			struct snd_pcm_hw_rule *rule)
 12{
 13	u8 **formats = 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 = 1
 20	};
 21	struct snd_oxfw_stream_formation formation;
 22	int i, err;
 23
 24	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
 25		if (formats[i] == NULL)
 26			continue;
 27
 28		err = snd_oxfw_stream_parse_format(formats[i], &formation);
 29		if (err < 0)
 30			continue;
 31		if (!snd_interval_test(c, formation.pcm))
 32			continue;
 33
 34		t.min = min(t.min, formation.rate);
 35		t.max = max(t.max, formation.rate);
 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	u8 **formats = 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_oxfw_stream_formation formation;
 50	int i, j, err;
 51	unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0};
 52
 53	count = 0;
 54	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
 55		if (formats[i] == NULL)
 56			break;
 57
 58		err = snd_oxfw_stream_parse_format(formats[i], &formation);
 59		if (err < 0)
 60			continue;
 61		if (!snd_interval_test(r, formation.rate))
 62			continue;
 63		if (list[count] == formation.pcm)
 64			continue;
 65
 66		for (j = 0; j < ARRAY_SIZE(list); j++) {
 67			if (list[j] == formation.pcm)
 68				break;
 69		}
 70		if (j == ARRAY_SIZE(list)) {
 71			list[count] = formation.pcm;
 72			if (++count == ARRAY_SIZE(list))
 73				break;
 74		}
 75	}
 76
 77	return snd_interval_list(c, count, list, 0);
 78}
 79
 80static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats)
 81{
 82	struct snd_oxfw_stream_formation formation;
 83	int i, err;
 84
 85	hw->channels_min = UINT_MAX;
 86	hw->channels_max = 0;
 87
 88	hw->rate_min = UINT_MAX;
 89	hw->rate_max = 0;
 90	hw->rates = 0;
 91
 92	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
 93		if (formats[i] == NULL)
 94			break;
 95
 96		err = snd_oxfw_stream_parse_format(formats[i], &formation);
 97		if (err < 0)
 98			continue;
 99
100		hw->channels_min = min(hw->channels_min, formation.pcm);
101		hw->channels_max = max(hw->channels_max, formation.pcm);
102
103		hw->rate_min = min(hw->rate_min, formation.rate);
104		hw->rate_max = max(hw->rate_max, formation.rate);
105		hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate);
106	}
107}
108
109static int init_hw_params(struct snd_oxfw *oxfw,
110			  struct snd_pcm_substream *substream)
111{
112	struct snd_pcm_runtime *runtime = substream->runtime;
113	u8 **formats;
114	struct amdtp_stream *stream;
115	int err;
116
117	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
118		runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
119		stream = &oxfw->tx_stream;
120		formats = oxfw->tx_stream_formats;
121	} else {
122		runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
123		stream = &oxfw->rx_stream;
124		formats = oxfw->rx_stream_formats;
125	}
126
127	limit_channels_and_rates(&runtime->hw, formats);
128
129	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
130				  hw_rule_channels, formats,
131				  SNDRV_PCM_HW_PARAM_RATE, -1);
132	if (err < 0)
133		goto end;
134
135	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
136				  hw_rule_rate, formats,
137				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
138	if (err < 0)
139		goto end;
140
141	err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
142end:
143	return err;
144}
145
146static int limit_to_current_params(struct snd_pcm_substream *substream)
147{
148	struct snd_oxfw *oxfw = substream->private_data;
149	struct snd_oxfw_stream_formation formation;
150	enum avc_general_plug_dir dir;
151	int err;
152
153	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
154		dir = AVC_GENERAL_PLUG_DIR_OUT;
155	else
156		dir = AVC_GENERAL_PLUG_DIR_IN;
157
158	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
159	if (err < 0)
160		goto end;
161
162	substream->runtime->hw.channels_min = formation.pcm;
163	substream->runtime->hw.channels_max = formation.pcm;
164	substream->runtime->hw.rate_min = formation.rate;
165	substream->runtime->hw.rate_max = formation.rate;
166end:
167	return err;
168}
169
170static int pcm_open(struct snd_pcm_substream *substream)
171{
172	struct snd_oxfw *oxfw = substream->private_data;
173	struct amdtp_domain *d = &oxfw->domain;
174	int err;
175
176	err = snd_oxfw_stream_lock_try(oxfw);
177	if (err < 0)
178		return err;
179
180	err = init_hw_params(oxfw, substream);
181	if (err < 0)
182		goto err_locked;
183
184	scoped_guard(mutex, &oxfw->mutex) {
185		// When source of clock is not internal or any stream is reserved for
186		// transmission of PCM frames, the available sampling rate is limited
187		// at current one.
188		if (oxfw->substreams_count > 0 && d->events_per_period > 0) {
189			unsigned int frames_per_period = d->events_per_period;
190			unsigned int frames_per_buffer = d->events_per_buffer;
191
192			err = limit_to_current_params(substream);
193			if (err < 0)
194				goto err_locked;
195
196			if (frames_per_period > 0) {
197				err = snd_pcm_hw_constraint_minmax(substream->runtime,
198								   SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
199								   frames_per_period, frames_per_period);
200				if (err < 0)
201					goto err_locked;
202
203				err = snd_pcm_hw_constraint_minmax(substream->runtime,
204								   SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
205								   frames_per_buffer, frames_per_buffer);
206				if (err < 0)
207					goto err_locked;
208			}
209		}
210	}
211
212	snd_pcm_set_sync(substream);
213
214	return 0;
215err_locked:
216	snd_oxfw_stream_lock_release(oxfw);
217	return err;
218}
219
220static int pcm_close(struct snd_pcm_substream *substream)
221{
222	struct snd_oxfw *oxfw = substream->private_data;
223
224	snd_oxfw_stream_lock_release(oxfw);
225	return 0;
226}
227
228static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
229				 struct snd_pcm_hw_params *hw_params)
230{
231	struct snd_oxfw *oxfw = substream->private_data;
232	int err = 0;
233
234	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
235		unsigned int rate = params_rate(hw_params);
236		unsigned int channels = params_channels(hw_params);
237		unsigned int frames_per_period = params_period_size(hw_params);
238		unsigned int frames_per_buffer = params_buffer_size(hw_params);
239
240		guard(mutex)(&oxfw->mutex);
241		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
242					rate, channels, frames_per_period,
243					frames_per_buffer);
244		if (err >= 0)
245			++oxfw->substreams_count;
246	}
247
248	return err;
249}
250static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
251				  struct snd_pcm_hw_params *hw_params)
252{
253	struct snd_oxfw *oxfw = substream->private_data;
254	int err = 0;
255
256	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
257		unsigned int rate = params_rate(hw_params);
258		unsigned int channels = params_channels(hw_params);
259		unsigned int frames_per_period = params_period_size(hw_params);
260		unsigned int frames_per_buffer = params_buffer_size(hw_params);
261
262		guard(mutex)(&oxfw->mutex);
263		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream,
264					rate, channels, frames_per_period,
265					frames_per_buffer);
266		if (err >= 0)
267			++oxfw->substreams_count;
268	}
269
270	return err;
271}
272
273static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
274{
275	struct snd_oxfw *oxfw = substream->private_data;
276
277	guard(mutex)(&oxfw->mutex);
278
279	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
280		--oxfw->substreams_count;
281
282	snd_oxfw_stream_stop_duplex(oxfw);
283
284	return 0;
285}
286static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
287{
288	struct snd_oxfw *oxfw = substream->private_data;
289
290	guard(mutex)(&oxfw->mutex);
291
292	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
293		--oxfw->substreams_count;
294
295	snd_oxfw_stream_stop_duplex(oxfw);
296
297	return 0;
298}
299
300static int pcm_capture_prepare(struct snd_pcm_substream *substream)
301{
302	struct snd_oxfw *oxfw = substream->private_data;
303	int err;
304
305	scoped_guard(mutex, &oxfw->mutex) {
306		err = snd_oxfw_stream_start_duplex(oxfw);
307		if (err < 0)
308			return err;
309	}
310
311	amdtp_stream_pcm_prepare(&oxfw->tx_stream);
312	return 0;
313}
314static int pcm_playback_prepare(struct snd_pcm_substream *substream)
315{
316	struct snd_oxfw *oxfw = substream->private_data;
317	int err;
318
319	scoped_guard(mutex, &oxfw->mutex) {
320		err = snd_oxfw_stream_start_duplex(oxfw);
321		if (err < 0)
322			return err;
323	}
324
325	amdtp_stream_pcm_prepare(&oxfw->rx_stream);
326	return 0;
327}
328
329static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
330{
331	struct snd_oxfw *oxfw = substream->private_data;
332	struct snd_pcm_substream *pcm;
333
334	switch (cmd) {
335	case SNDRV_PCM_TRIGGER_START:
336		pcm = substream;
337		break;
338	case SNDRV_PCM_TRIGGER_STOP:
339		pcm = NULL;
340		break;
341	default:
342		return -EINVAL;
343	}
344	amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);
345	return 0;
346}
347static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
348{
349	struct snd_oxfw *oxfw = substream->private_data;
350	struct snd_pcm_substream *pcm;
351
352	switch (cmd) {
353	case SNDRV_PCM_TRIGGER_START:
354		pcm = substream;
355		break;
356	case SNDRV_PCM_TRIGGER_STOP:
357		pcm = NULL;
358		break;
359	default:
360		return -EINVAL;
361	}
362	amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);
363	return 0;
364}
365
366static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
367{
368	struct snd_oxfw *oxfw = sbstm->private_data;
369
370	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream);
371}
372static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
373{
374	struct snd_oxfw *oxfw = sbstm->private_data;
375
376	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream);
377}
378
379static int pcm_capture_ack(struct snd_pcm_substream *substream)
380{
381	struct snd_oxfw *oxfw = substream->private_data;
382
383	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream);
384}
385
386static int pcm_playback_ack(struct snd_pcm_substream *substream)
387{
388	struct snd_oxfw *oxfw = substream->private_data;
389
390	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);
391}
392
393int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
394{
395	static const struct snd_pcm_ops capture_ops = {
396		.open      = pcm_open,
397		.close     = pcm_close,
398		.hw_params = pcm_capture_hw_params,
399		.hw_free   = pcm_capture_hw_free,
400		.prepare   = pcm_capture_prepare,
401		.trigger   = pcm_capture_trigger,
402		.pointer   = pcm_capture_pointer,
403		.ack       = pcm_capture_ack,
404	};
405	static const struct snd_pcm_ops playback_ops = {
406		.open      = pcm_open,
407		.close     = pcm_close,
408		.hw_params = pcm_playback_hw_params,
409		.hw_free   = pcm_playback_hw_free,
410		.prepare   = pcm_playback_prepare,
411		.trigger   = pcm_playback_trigger,
412		.pointer   = pcm_playback_pointer,
413		.ack       = pcm_playback_ack,
414	};
415	struct snd_pcm *pcm;
416	unsigned int cap = 0;
417	int err;
418
419	if (oxfw->has_output)
420		cap = 1;
421
422	err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
423	if (err < 0)
424		return err;
425
426	pcm->private_data = oxfw;
427	pcm->nonatomic = true;
428	strscpy(pcm->name, oxfw->card->shortname);
429	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
430	if (cap > 0)
431		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
432	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
433
434	return 0;
435}
~/ Read-only file preview.