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	mutex_lock(&oxfw->mutex);
185
186	// When source of clock is not internal or any stream is reserved for
187	// transmission of PCM frames, the available sampling rate is limited
188	// at current one.
189	if (oxfw->substreams_count > 0 && d->events_per_period > 0) {
190		unsigned int frames_per_period = d->events_per_period;
191		unsigned int frames_per_buffer = d->events_per_buffer;
192
193		err = limit_to_current_params(substream);
194		if (err < 0) {
195			mutex_unlock(&oxfw->mutex);
196			goto err_locked;
197		}
198
199		if (frames_per_period > 0) {
200			err = snd_pcm_hw_constraint_minmax(substream->runtime,
201					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
202					frames_per_period, frames_per_period);
203			if (err < 0) {
204				mutex_unlock(&oxfw->mutex);
205				goto err_locked;
206			}
207
208			err = snd_pcm_hw_constraint_minmax(substream->runtime,
209					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
210					frames_per_buffer, frames_per_buffer);
211			if (err < 0) {
212				mutex_unlock(&oxfw->mutex);
213				goto err_locked;
214			}
215		}
216	}
217
218	mutex_unlock(&oxfw->mutex);
219
220	snd_pcm_set_sync(substream);
221
222	return 0;
223err_locked:
224	snd_oxfw_stream_lock_release(oxfw);
225	return err;
226}
227
228static int pcm_close(struct snd_pcm_substream *substream)
229{
230	struct snd_oxfw *oxfw = substream->private_data;
231
232	snd_oxfw_stream_lock_release(oxfw);
233	return 0;
234}
235
236static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
237				 struct snd_pcm_hw_params *hw_params)
238{
239	struct snd_oxfw *oxfw = substream->private_data;
240	int err = 0;
241
242	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
243		unsigned int rate = params_rate(hw_params);
244		unsigned int channels = params_channels(hw_params);
245		unsigned int frames_per_period = params_period_size(hw_params);
246		unsigned int frames_per_buffer = params_buffer_size(hw_params);
247
248		mutex_lock(&oxfw->mutex);
249		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
250					rate, channels, frames_per_period,
251					frames_per_buffer);
252		if (err >= 0)
253			++oxfw->substreams_count;
254		mutex_unlock(&oxfw->mutex);
255	}
256
257	return err;
258}
259static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
260				  struct snd_pcm_hw_params *hw_params)
261{
262	struct snd_oxfw *oxfw = substream->private_data;
263	int err = 0;
264
265	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
266		unsigned int rate = params_rate(hw_params);
267		unsigned int channels = params_channels(hw_params);
268		unsigned int frames_per_period = params_period_size(hw_params);
269		unsigned int frames_per_buffer = params_buffer_size(hw_params);
270
271		mutex_lock(&oxfw->mutex);
272		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream,
273					rate, channels, frames_per_period,
274					frames_per_buffer);
275		if (err >= 0)
276			++oxfw->substreams_count;
277		mutex_unlock(&oxfw->mutex);
278	}
279
280	return err;
281}
282
283static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
284{
285	struct snd_oxfw *oxfw = substream->private_data;
286
287	mutex_lock(&oxfw->mutex);
288
289	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
290		--oxfw->substreams_count;
291
292	snd_oxfw_stream_stop_duplex(oxfw);
293
294	mutex_unlock(&oxfw->mutex);
295
296	return 0;
297}
298static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
299{
300	struct snd_oxfw *oxfw = substream->private_data;
301
302	mutex_lock(&oxfw->mutex);
303
304	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
305		--oxfw->substreams_count;
306
307	snd_oxfw_stream_stop_duplex(oxfw);
308
309	mutex_unlock(&oxfw->mutex);
310
311	return 0;
312}
313
314static int pcm_capture_prepare(struct snd_pcm_substream *substream)
315{
316	struct snd_oxfw *oxfw = substream->private_data;
317	int err;
318
319	mutex_lock(&oxfw->mutex);
320	err = snd_oxfw_stream_start_duplex(oxfw);
321	mutex_unlock(&oxfw->mutex);
322	if (err < 0)
323		goto end;
324
325	amdtp_stream_pcm_prepare(&oxfw->tx_stream);
326end:
327	return err;
328}
329static int pcm_playback_prepare(struct snd_pcm_substream *substream)
330{
331	struct snd_oxfw *oxfw = substream->private_data;
332	int err;
333
334	mutex_lock(&oxfw->mutex);
335	err = snd_oxfw_stream_start_duplex(oxfw);
336	mutex_unlock(&oxfw->mutex);
337	if (err < 0)
338		goto end;
339
340	amdtp_stream_pcm_prepare(&oxfw->rx_stream);
341end:
342	return err;
343}
344
345static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
346{
347	struct snd_oxfw *oxfw = substream->private_data;
348	struct snd_pcm_substream *pcm;
349
350	switch (cmd) {
351	case SNDRV_PCM_TRIGGER_START:
352		pcm = substream;
353		break;
354	case SNDRV_PCM_TRIGGER_STOP:
355		pcm = NULL;
356		break;
357	default:
358		return -EINVAL;
359	}
360	amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);
361	return 0;
362}
363static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
364{
365	struct snd_oxfw *oxfw = substream->private_data;
366	struct snd_pcm_substream *pcm;
367
368	switch (cmd) {
369	case SNDRV_PCM_TRIGGER_START:
370		pcm = substream;
371		break;
372	case SNDRV_PCM_TRIGGER_STOP:
373		pcm = NULL;
374		break;
375	default:
376		return -EINVAL;
377	}
378	amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);
379	return 0;
380}
381
382static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
383{
384	struct snd_oxfw *oxfw = sbstm->private_data;
385
386	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream);
387}
388static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
389{
390	struct snd_oxfw *oxfw = sbstm->private_data;
391
392	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream);
393}
394
395static int pcm_capture_ack(struct snd_pcm_substream *substream)
396{
397	struct snd_oxfw *oxfw = substream->private_data;
398
399	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream);
400}
401
402static int pcm_playback_ack(struct snd_pcm_substream *substream)
403{
404	struct snd_oxfw *oxfw = substream->private_data;
405
406	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);
407}
408
409int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
410{
411	static const struct snd_pcm_ops capture_ops = {
412		.open      = pcm_open,
413		.close     = pcm_close,
414		.hw_params = pcm_capture_hw_params,
415		.hw_free   = pcm_capture_hw_free,
416		.prepare   = pcm_capture_prepare,
417		.trigger   = pcm_capture_trigger,
418		.pointer   = pcm_capture_pointer,
419		.ack       = pcm_capture_ack,
420	};
421	static const struct snd_pcm_ops playback_ops = {
422		.open      = pcm_open,
423		.close     = pcm_close,
424		.hw_params = pcm_playback_hw_params,
425		.hw_free   = pcm_playback_hw_free,
426		.prepare   = pcm_playback_prepare,
427		.trigger   = pcm_playback_trigger,
428		.pointer   = pcm_playback_pointer,
429		.ack       = pcm_playback_ack,
430	};
431	struct snd_pcm *pcm;
432	unsigned int cap = 0;
433	int err;
434
435	if (oxfw->has_output)
436		cap = 1;
437
438	err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
439	if (err < 0)
440		return err;
441
442	pcm->private_data = oxfw;
443	strcpy(pcm->name, oxfw->card->shortname);
444	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
445	if (cap > 0)
446		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
447	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
448
449	return 0;
450}
451