• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * tascam-pcm.c - a part of driver for TASCAM FireWire series
3  *
4  * Copyright (c) 2015 Takashi Sakamoto
5  *
6  * Licensed under the terms of the GNU General Public License, version 2.
7  */
8 
9 #include "tascam.h"
10 
pcm_init_hw_params(struct snd_tscm * tscm,struct snd_pcm_substream * substream)11 static int pcm_init_hw_params(struct snd_tscm *tscm,
12 			      struct snd_pcm_substream *substream)
13 {
14 	struct snd_pcm_runtime *runtime = substream->runtime;
15 	struct snd_pcm_hardware *hw = &runtime->hw;
16 	struct amdtp_stream *stream;
17 	unsigned int pcm_channels;
18 
19 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
20 		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
21 		stream = &tscm->tx_stream;
22 		pcm_channels = tscm->spec->pcm_capture_analog_channels;
23 	} else {
24 		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
25 		stream = &tscm->rx_stream;
26 		pcm_channels = tscm->spec->pcm_playback_analog_channels;
27 	}
28 
29 	if (tscm->spec->has_adat)
30 		pcm_channels += 8;
31 	if (tscm->spec->has_spdif)
32 		pcm_channels += 2;
33 	runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
34 
35 	hw->rates = SNDRV_PCM_RATE_44100 |
36 		    SNDRV_PCM_RATE_48000 |
37 		    SNDRV_PCM_RATE_88200 |
38 		    SNDRV_PCM_RATE_96000;
39 	snd_pcm_limit_hw_rates(runtime);
40 
41 	return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
42 }
43 
pcm_open(struct snd_pcm_substream * substream)44 static int pcm_open(struct snd_pcm_substream *substream)
45 {
46 	struct snd_tscm *tscm = substream->private_data;
47 	enum snd_tscm_clock clock;
48 	unsigned int rate;
49 	int err;
50 
51 	err = snd_tscm_stream_lock_try(tscm);
52 	if (err < 0)
53 		goto end;
54 
55 	err = pcm_init_hw_params(tscm, substream);
56 	if (err < 0)
57 		goto err_locked;
58 
59 	err = snd_tscm_stream_get_clock(tscm, &clock);
60 	if (err < 0)
61 		goto err_locked;
62 
63 	if (clock != SND_TSCM_CLOCK_INTERNAL ||
64 	    amdtp_stream_pcm_running(&tscm->rx_stream) ||
65 	    amdtp_stream_pcm_running(&tscm->tx_stream)) {
66 		err = snd_tscm_stream_get_rate(tscm, &rate);
67 		if (err < 0)
68 			goto err_locked;
69 		substream->runtime->hw.rate_min = rate;
70 		substream->runtime->hw.rate_max = rate;
71 	}
72 
73 	snd_pcm_set_sync(substream);
74 end:
75 	return err;
76 err_locked:
77 	snd_tscm_stream_lock_release(tscm);
78 	return err;
79 }
80 
pcm_close(struct snd_pcm_substream * substream)81 static int pcm_close(struct snd_pcm_substream *substream)
82 {
83 	struct snd_tscm *tscm = substream->private_data;
84 
85 	snd_tscm_stream_lock_release(tscm);
86 
87 	return 0;
88 }
89 
pcm_capture_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)90 static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
91 				 struct snd_pcm_hw_params *hw_params)
92 {
93 	struct snd_tscm *tscm = substream->private_data;
94 	int err;
95 
96 	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
97 					       params_buffer_bytes(hw_params));
98 	if (err < 0)
99 		return err;
100 
101 	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
102 		mutex_lock(&tscm->mutex);
103 		tscm->substreams_counter++;
104 		mutex_unlock(&tscm->mutex);
105 	}
106 
107 	return 0;
108 }
109 
pcm_playback_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)110 static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
111 				  struct snd_pcm_hw_params *hw_params)
112 {
113 	struct snd_tscm *tscm = substream->private_data;
114 	int err;
115 
116 	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
117 					       params_buffer_bytes(hw_params));
118 	if (err < 0)
119 		return err;
120 
121 	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
122 		mutex_lock(&tscm->mutex);
123 		tscm->substreams_counter++;
124 		mutex_unlock(&tscm->mutex);
125 	}
126 
127 	return 0;
128 }
129 
pcm_capture_hw_free(struct snd_pcm_substream * substream)130 static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
131 {
132 	struct snd_tscm *tscm = substream->private_data;
133 
134 	mutex_lock(&tscm->mutex);
135 
136 	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
137 		tscm->substreams_counter--;
138 
139 	snd_tscm_stream_stop_duplex(tscm);
140 
141 	mutex_unlock(&tscm->mutex);
142 
143 	return snd_pcm_lib_free_vmalloc_buffer(substream);
144 }
145 
pcm_playback_hw_free(struct snd_pcm_substream * substream)146 static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
147 {
148 	struct snd_tscm *tscm = substream->private_data;
149 
150 	mutex_lock(&tscm->mutex);
151 
152 	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
153 		tscm->substreams_counter--;
154 
155 	snd_tscm_stream_stop_duplex(tscm);
156 
157 	mutex_unlock(&tscm->mutex);
158 
159 	return snd_pcm_lib_free_vmalloc_buffer(substream);
160 }
161 
pcm_capture_prepare(struct snd_pcm_substream * substream)162 static int pcm_capture_prepare(struct snd_pcm_substream *substream)
163 {
164 	struct snd_tscm *tscm = substream->private_data;
165 	struct snd_pcm_runtime *runtime = substream->runtime;
166 	int err;
167 
168 	mutex_lock(&tscm->mutex);
169 
170 	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
171 	if (err >= 0)
172 		amdtp_stream_pcm_prepare(&tscm->tx_stream);
173 
174 	mutex_unlock(&tscm->mutex);
175 
176 	return err;
177 }
178 
pcm_playback_prepare(struct snd_pcm_substream * substream)179 static int pcm_playback_prepare(struct snd_pcm_substream *substream)
180 {
181 	struct snd_tscm *tscm = substream->private_data;
182 	struct snd_pcm_runtime *runtime = substream->runtime;
183 	int err;
184 
185 	mutex_lock(&tscm->mutex);
186 
187 	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
188 	if (err >= 0)
189 		amdtp_stream_pcm_prepare(&tscm->rx_stream);
190 
191 	mutex_unlock(&tscm->mutex);
192 
193 	return err;
194 }
195 
pcm_capture_trigger(struct snd_pcm_substream * substream,int cmd)196 static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
197 {
198 	struct snd_tscm *tscm = substream->private_data;
199 
200 	switch (cmd) {
201 	case SNDRV_PCM_TRIGGER_START:
202 		amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
203 		break;
204 	case SNDRV_PCM_TRIGGER_STOP:
205 		amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
206 		break;
207 	default:
208 		return -EINVAL;
209 	}
210 
211 	return 0;
212 }
213 
pcm_playback_trigger(struct snd_pcm_substream * substream,int cmd)214 static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
215 {
216 	struct snd_tscm *tscm = substream->private_data;
217 
218 	switch (cmd) {
219 	case SNDRV_PCM_TRIGGER_START:
220 		amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
221 		break;
222 	case SNDRV_PCM_TRIGGER_STOP:
223 		amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
224 		break;
225 	default:
226 		return -EINVAL;
227 	}
228 
229 	return 0;
230 }
231 
pcm_capture_pointer(struct snd_pcm_substream * sbstrm)232 static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
233 {
234 	struct snd_tscm *tscm = sbstrm->private_data;
235 
236 	return amdtp_stream_pcm_pointer(&tscm->tx_stream);
237 }
238 
pcm_playback_pointer(struct snd_pcm_substream * sbstrm)239 static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
240 {
241 	struct snd_tscm *tscm = sbstrm->private_data;
242 
243 	return amdtp_stream_pcm_pointer(&tscm->rx_stream);
244 }
245 
pcm_capture_ack(struct snd_pcm_substream * substream)246 static int pcm_capture_ack(struct snd_pcm_substream *substream)
247 {
248 	struct snd_tscm *tscm = substream->private_data;
249 
250 	return amdtp_stream_pcm_ack(&tscm->tx_stream);
251 }
252 
pcm_playback_ack(struct snd_pcm_substream * substream)253 static int pcm_playback_ack(struct snd_pcm_substream *substream)
254 {
255 	struct snd_tscm *tscm = substream->private_data;
256 
257 	return amdtp_stream_pcm_ack(&tscm->rx_stream);
258 }
259 
snd_tscm_create_pcm_devices(struct snd_tscm * tscm)260 int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
261 {
262 	static const struct snd_pcm_ops capture_ops = {
263 		.open		= pcm_open,
264 		.close		= pcm_close,
265 		.ioctl		= snd_pcm_lib_ioctl,
266 		.hw_params	= pcm_capture_hw_params,
267 		.hw_free	= pcm_capture_hw_free,
268 		.prepare	= pcm_capture_prepare,
269 		.trigger	= pcm_capture_trigger,
270 		.pointer	= pcm_capture_pointer,
271 		.ack		= pcm_capture_ack,
272 		.page		= snd_pcm_lib_get_vmalloc_page,
273 	};
274 	static const struct snd_pcm_ops playback_ops = {
275 		.open		= pcm_open,
276 		.close		= pcm_close,
277 		.ioctl		= snd_pcm_lib_ioctl,
278 		.hw_params	= pcm_playback_hw_params,
279 		.hw_free	= pcm_playback_hw_free,
280 		.prepare	= pcm_playback_prepare,
281 		.trigger	= pcm_playback_trigger,
282 		.pointer	= pcm_playback_pointer,
283 		.ack		= pcm_playback_ack,
284 		.page		= snd_pcm_lib_get_vmalloc_page,
285 	};
286 	struct snd_pcm *pcm;
287 	int err;
288 
289 	err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
290 	if (err < 0)
291 		return err;
292 
293 	pcm->private_data = tscm;
294 	snprintf(pcm->name, sizeof(pcm->name),
295 		 "%s PCM", tscm->card->shortname);
296 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
297 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
298 
299 	return 0;
300 }
301