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