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