aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2014-12-08 10:10:47 -0500
committerTakashi Iwai <tiwai@suse.de>2014-12-10 04:49:17 -0500
commit216e256f7bf974ba402309d0ceb24f3500dc65c4 (patch)
tree170e0a8c901f75e6731b162f20a6d9a67721bea2 /sound
parentb0ac00095fe1485f60bb8ea7326426d3d02a1aec (diff)
ALSA: oxfw: add support for capturing PCM samples
In previous commit, a support for transmitted packets is added. This commit add a support for capturing PCM samples. When any streams are already started, this driver should not change sampling rate of the device, thus this commit also adds a restriction of sampling rate in this situation. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Acked-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/firewire/oxfw/oxfw-pcm.c200
1 files changed, 180 insertions, 20 deletions
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index a78339c81de8..e84fc9c4bfd1 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -118,21 +118,31 @@ static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
118 hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; 118 hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
119} 119}
120 120
121static int pcm_open(struct snd_pcm_substream *substream) 121static int init_hw_params(struct snd_oxfw *oxfw,
122 struct snd_pcm_substream *substream)
122{ 123{
123 struct snd_oxfw *oxfw = substream->private_data;
124 struct snd_pcm_runtime *runtime = substream->runtime; 124 struct snd_pcm_runtime *runtime = substream->runtime;
125 u8 **formats; 125 u8 **formats;
126 struct amdtp_stream *stream;
126 int err; 127 int err;
127 128
128 formats = oxfw->rx_stream_formats;
129
130 runtime->hw.info = SNDRV_PCM_INFO_BATCH | 129 runtime->hw.info = SNDRV_PCM_INFO_BATCH |
131 SNDRV_PCM_INFO_BLOCK_TRANSFER | 130 SNDRV_PCM_INFO_BLOCK_TRANSFER |
132 SNDRV_PCM_INFO_INTERLEAVED | 131 SNDRV_PCM_INFO_INTERLEAVED |
132 SNDRV_PCM_INFO_JOINT_DUPLEX |
133 SNDRV_PCM_INFO_MMAP | 133 SNDRV_PCM_INFO_MMAP |
134 SNDRV_PCM_INFO_MMAP_VALID; 134 SNDRV_PCM_INFO_MMAP_VALID;
135 135
136 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
137 runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
138 stream = &oxfw->tx_stream;
139 formats = oxfw->tx_stream_formats;
140 } else {
141 runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
142 stream = &oxfw->rx_stream;
143 formats = oxfw->rx_stream_formats;
144 }
145
136 limit_channels_and_rates(&runtime->hw, formats); 146 limit_channels_and_rates(&runtime->hw, formats);
137 limit_period_and_buffer(&runtime->hw); 147 limit_period_and_buffer(&runtime->hw);
138 148
@@ -148,10 +158,55 @@ static int pcm_open(struct snd_pcm_substream *substream)
148 if (err < 0) 158 if (err < 0)
149 goto end; 159 goto end;
150 160
151 err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime); 161 err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
162end:
163 return err;
164}
165
166static int limit_to_current_params(struct snd_pcm_substream *substream)
167{
168 struct snd_oxfw *oxfw = substream->private_data;
169 struct snd_oxfw_stream_formation formation;
170 enum avc_general_plug_dir dir;
171 int err;
172
173 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
174 dir = AVC_GENERAL_PLUG_DIR_OUT;
175 else
176 dir = AVC_GENERAL_PLUG_DIR_IN;
177
178 err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
179 if (err < 0)
180 goto end;
181
182 substream->runtime->hw.channels_min = formation.pcm;
183 substream->runtime->hw.channels_max = formation.pcm;
184 substream->runtime->hw.rate_min = formation.rate;
185 substream->runtime->hw.rate_max = formation.rate;
186end:
187 return err;
188}
189
190static int pcm_open(struct snd_pcm_substream *substream)
191{
192 struct snd_oxfw *oxfw = substream->private_data;
193 int err;
194
195 err = init_hw_params(oxfw, substream);
152 if (err < 0) 196 if (err < 0)
153 goto end; 197 goto end;
154 198
199 /*
200 * When any PCM streams are already running, the available sampling
201 * rate is limited at current value.
202 */
203 if (amdtp_stream_pcm_running(&oxfw->tx_stream) ||
204 amdtp_stream_pcm_running(&oxfw->rx_stream)) {
205 err = limit_to_current_params(substream);
206 if (err < 0)
207 goto end;
208 }
209
155 snd_pcm_set_sync(substream); 210 snd_pcm_set_sync(substream);
156end: 211end:
157 return err; 212 return err;
@@ -162,28 +217,89 @@ static int pcm_close(struct snd_pcm_substream *substream)
162 return 0; 217 return 0;
163} 218}
164 219
165static int pcm_hw_params(struct snd_pcm_substream *substream, 220static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
166 struct snd_pcm_hw_params *hw_params) 221 struct snd_pcm_hw_params *hw_params)
167{ 222{
168 struct snd_oxfw *oxfw = substream->private_data; 223 struct snd_oxfw *oxfw = substream->private_data;
169 224
225
226 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
227 mutex_lock(&oxfw->mutex);
228 oxfw->capture_substreams++;
229 mutex_unlock(&oxfw->mutex);
230 }
231
232 amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
233
234 return snd_pcm_lib_alloc_vmalloc_buffer(substream,
235 params_buffer_bytes(hw_params));
236}
237static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
238 struct snd_pcm_hw_params *hw_params)
239{
240 struct snd_oxfw *oxfw = substream->private_data;
241
242 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
243 mutex_lock(&oxfw->mutex);
244 oxfw->playback_substreams++;
245 mutex_unlock(&oxfw->mutex);
246 }
247
170 amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params)); 248 amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
249
171 return snd_pcm_lib_alloc_vmalloc_buffer(substream, 250 return snd_pcm_lib_alloc_vmalloc_buffer(substream,
172 params_buffer_bytes(hw_params)); 251 params_buffer_bytes(hw_params));
173} 252}
174 253
175static int pcm_hw_free(struct snd_pcm_substream *substream) 254static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
255{
256 struct snd_oxfw *oxfw = substream->private_data;
257
258 mutex_lock(&oxfw->mutex);
259
260 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
261 oxfw->capture_substreams--;
262
263 snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
264
265 mutex_unlock(&oxfw->mutex);
266
267 return snd_pcm_lib_free_vmalloc_buffer(substream);
268}
269static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
176{ 270{
177 struct snd_oxfw *oxfw = substream->private_data; 271 struct snd_oxfw *oxfw = substream->private_data;
178 272
179 mutex_lock(&oxfw->mutex); 273 mutex_lock(&oxfw->mutex);
274
275 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
276 oxfw->playback_substreams--;
277
180 snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream); 278 snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
279
181 mutex_unlock(&oxfw->mutex); 280 mutex_unlock(&oxfw->mutex);
182 281
183 return snd_pcm_lib_free_vmalloc_buffer(substream); 282 return snd_pcm_lib_free_vmalloc_buffer(substream);
184} 283}
185 284
186static int pcm_prepare(struct snd_pcm_substream *substream) 285static int pcm_capture_prepare(struct snd_pcm_substream *substream)
286{
287 struct snd_oxfw *oxfw = substream->private_data;
288 struct snd_pcm_runtime *runtime = substream->runtime;
289 int err;
290
291 mutex_lock(&oxfw->mutex);
292 err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream,
293 runtime->rate, runtime->channels);
294 mutex_unlock(&oxfw->mutex);
295 if (err < 0)
296 goto end;
297
298 amdtp_stream_pcm_prepare(&oxfw->tx_stream);
299end:
300 return err;
301}
302static int pcm_playback_prepare(struct snd_pcm_substream *substream)
187{ 303{
188 struct snd_oxfw *oxfw = substream->private_data; 304 struct snd_oxfw *oxfw = substream->private_data;
189 struct snd_pcm_runtime *runtime = substream->runtime; 305 struct snd_pcm_runtime *runtime = substream->runtime;
@@ -201,7 +317,25 @@ end:
201 return err; 317 return err;
202} 318}
203 319
204static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) 320static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
321{
322 struct snd_oxfw *oxfw = substream->private_data;
323 struct snd_pcm_substream *pcm;
324
325 switch (cmd) {
326 case SNDRV_PCM_TRIGGER_START:
327 pcm = substream;
328 break;
329 case SNDRV_PCM_TRIGGER_STOP:
330 pcm = NULL;
331 break;
332 default:
333 return -EINVAL;
334 }
335 amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);
336 return 0;
337}
338static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
205{ 339{
206 struct snd_oxfw *oxfw = substream->private_data; 340 struct snd_oxfw *oxfw = substream->private_data;
207 struct snd_pcm_substream *pcm; 341 struct snd_pcm_substream *pcm;
@@ -220,35 +354,61 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
220 return 0; 354 return 0;
221} 355}
222 356
223static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) 357static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
224{ 358{
225 struct snd_oxfw *oxfw = substream->private_data; 359 struct snd_oxfw *oxfw = sbstm->private_data;
360
361 return amdtp_stream_pcm_pointer(&oxfw->tx_stream);
362}
363static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
364{
365 struct snd_oxfw *oxfw = sbstm->private_data;
226 366
227 return amdtp_stream_pcm_pointer(&oxfw->rx_stream); 367 return amdtp_stream_pcm_pointer(&oxfw->rx_stream);
228} 368}
229 369
230int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) 370int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
231{ 371{
232 static struct snd_pcm_ops ops = { 372 static struct snd_pcm_ops capture_ops = {
373 .open = pcm_open,
374 .close = pcm_close,
375 .ioctl = snd_pcm_lib_ioctl,
376 .hw_params = pcm_capture_hw_params,
377 .hw_free = pcm_capture_hw_free,
378 .prepare = pcm_capture_prepare,
379 .trigger = pcm_capture_trigger,
380 .pointer = pcm_capture_pointer,
381 .page = snd_pcm_lib_get_vmalloc_page,
382 .mmap = snd_pcm_lib_mmap_vmalloc,
383 };
384 static struct snd_pcm_ops playback_ops = {
233 .open = pcm_open, 385 .open = pcm_open,
234 .close = pcm_close, 386 .close = pcm_close,
235 .ioctl = snd_pcm_lib_ioctl, 387 .ioctl = snd_pcm_lib_ioctl,
236 .hw_params = pcm_hw_params, 388 .hw_params = pcm_playback_hw_params,
237 .hw_free = pcm_hw_free, 389 .hw_free = pcm_playback_hw_free,
238 .prepare = pcm_prepare, 390 .prepare = pcm_playback_prepare,
239 .trigger = pcm_trigger, 391 .trigger = pcm_playback_trigger,
240 .pointer = pcm_pointer, 392 .pointer = pcm_playback_pointer,
241 .page = snd_pcm_lib_get_vmalloc_page, 393 .page = snd_pcm_lib_get_vmalloc_page,
242 .mmap = snd_pcm_lib_mmap_vmalloc, 394 .mmap = snd_pcm_lib_mmap_vmalloc,
243 }; 395 };
244 struct snd_pcm *pcm; 396 struct snd_pcm *pcm;
397 unsigned int cap = 0;
245 int err; 398 int err;
246 399
247 err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, 0, &pcm); 400 if (oxfw->has_output)
401 cap = 1;
402
403 err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
248 if (err < 0) 404 if (err < 0)
249 return err; 405 return err;
406
250 pcm->private_data = oxfw; 407 pcm->private_data = oxfw;
251 strcpy(pcm->name, oxfw->card->shortname); 408 strcpy(pcm->name, oxfw->card->shortname);
252 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); 409 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
410 if (cap > 0)
411 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
412
253 return 0; 413 return 0;
254} 414}