diff options
author | Takashi Sakamoto <o-takashi@sakamocchi.jp> | 2014-12-08 10:10:38 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2014-12-10 04:45:53 -0500 |
commit | 69dcf3e47a39f8f42e35245289691ca8321b46f1 (patch) | |
tree | 4233a3109db03138df5a162d153f2c1568cedd4e | |
parent | 8fc01fc0674e3ea7fdd13bd3d138793619227f89 (diff) |
ALSA: dice: Add support for capturing PCM samples
This commit adds a support for capturing PCM samples.
When opposite PCM substream is already running, available sampling rate is
limited at current one.
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Acked-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/firewire/Kconfig | 3 | ||||
-rw-r--r-- | sound/firewire/dice/dice-pcm.c | 161 |
2 files changed, 147 insertions, 17 deletions
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 2a5b9a6cb6f8..093286087bb0 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig | |||
@@ -20,9 +20,6 @@ config SND_DICE | |||
20 | Say Y here to include support for many DACs based on the DICE | 20 | Say Y here to include support for many DACs based on the DICE |
21 | chip family (DICE-II/Jr/Mini) which TC Applied Technologies produces. | 21 | chip family (DICE-II/Jr/Mini) which TC Applied Technologies produces. |
22 | 22 | ||
23 | At the moment, this driver supports playback only. If you | ||
24 | want to use devices that support capturing, use FFADO instead. | ||
25 | |||
26 | To compile this driver as a module, choose M here: the module | 23 | To compile this driver as a module, choose M here: the module |
27 | will be called snd-dice. | 24 | will be called snd-dice. |
28 | 25 | ||
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 062b7a3b7fd0..f77714511f8b 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c | |||
@@ -12,7 +12,8 @@ | |||
12 | static int dice_rate_constraint(struct snd_pcm_hw_params *params, | 12 | static int dice_rate_constraint(struct snd_pcm_hw_params *params, |
13 | struct snd_pcm_hw_rule *rule) | 13 | struct snd_pcm_hw_rule *rule) |
14 | { | 14 | { |
15 | struct snd_dice *dice = rule->private; | 15 | struct snd_pcm_substream *substream = rule->private; |
16 | struct snd_dice *dice = substream->private_data; | ||
16 | 17 | ||
17 | const struct snd_interval *c = | 18 | const struct snd_interval *c = |
18 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); | 19 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); |
@@ -21,7 +22,12 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, | |||
21 | struct snd_interval rates = { | 22 | struct snd_interval rates = { |
22 | .min = UINT_MAX, .max = 0, .integer = 1 | 23 | .min = UINT_MAX, .max = 0, .integer = 1 |
23 | }; | 24 | }; |
24 | unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; | 25 | unsigned int i, rate, mode, *pcm_channels; |
26 | |||
27 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
28 | pcm_channels = dice->tx_channels; | ||
29 | else | ||
30 | pcm_channels = dice->rx_channels; | ||
25 | 31 | ||
26 | for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { | 32 | for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { |
27 | rate = snd_dice_rates[i]; | 33 | rate = snd_dice_rates[i]; |
@@ -41,7 +47,8 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, | |||
41 | static int dice_channels_constraint(struct snd_pcm_hw_params *params, | 47 | static int dice_channels_constraint(struct snd_pcm_hw_params *params, |
42 | struct snd_pcm_hw_rule *rule) | 48 | struct snd_pcm_hw_rule *rule) |
43 | { | 49 | { |
44 | struct snd_dice *dice = rule->private; | 50 | struct snd_pcm_substream *substream = rule->private; |
51 | struct snd_dice *dice = substream->private_data; | ||
45 | 52 | ||
46 | const struct snd_interval *r = | 53 | const struct snd_interval *r = |
47 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); | 54 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); |
@@ -50,7 +57,12 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params, | |||
50 | struct snd_interval channels = { | 57 | struct snd_interval channels = { |
51 | .min = UINT_MAX, .max = 0, .integer = 1 | 58 | .min = UINT_MAX, .max = 0, .integer = 1 |
52 | }; | 59 | }; |
53 | unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; | 60 | unsigned int i, rate, mode, *pcm_channels; |
61 | |||
62 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
63 | pcm_channels = dice->tx_channels; | ||
64 | else | ||
65 | pcm_channels = dice->rx_channels; | ||
54 | 66 | ||
55 | for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { | 67 | for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { |
56 | rate = snd_dice_rates[i]; | 68 | rate = snd_dice_rates[i]; |
@@ -109,30 +121,42 @@ static int init_hw_info(struct snd_dice *dice, | |||
109 | { | 121 | { |
110 | struct snd_pcm_runtime *runtime = substream->runtime; | 122 | struct snd_pcm_runtime *runtime = substream->runtime; |
111 | struct snd_pcm_hardware *hw = &runtime->hw; | 123 | struct snd_pcm_hardware *hw = &runtime->hw; |
124 | struct amdtp_stream *stream; | ||
125 | unsigned int *pcm_channels; | ||
112 | int err; | 126 | int err; |
113 | 127 | ||
114 | hw->info = SNDRV_PCM_INFO_MMAP | | 128 | hw->info = SNDRV_PCM_INFO_MMAP | |
115 | SNDRV_PCM_INFO_MMAP_VALID | | 129 | SNDRV_PCM_INFO_MMAP_VALID | |
116 | SNDRV_PCM_INFO_BATCH | | 130 | SNDRV_PCM_INFO_BATCH | |
117 | SNDRV_PCM_INFO_INTERLEAVED | | 131 | SNDRV_PCM_INFO_INTERLEAVED | |
132 | SNDRV_PCM_INFO_JOINT_DUPLEX | | ||
118 | SNDRV_PCM_INFO_BLOCK_TRANSFER; | 133 | SNDRV_PCM_INFO_BLOCK_TRANSFER; |
119 | hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; | ||
120 | 134 | ||
121 | limit_channels_and_rates(dice, runtime, dice->rx_channels); | 135 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
136 | hw->formats = AMDTP_IN_PCM_FORMAT_BITS; | ||
137 | stream = &dice->tx_stream; | ||
138 | pcm_channels = dice->tx_channels; | ||
139 | } else { | ||
140 | hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; | ||
141 | stream = &dice->rx_stream; | ||
142 | pcm_channels = dice->rx_channels; | ||
143 | } | ||
144 | |||
145 | limit_channels_and_rates(dice, runtime, pcm_channels); | ||
122 | limit_period_and_buffer(hw); | 146 | limit_period_and_buffer(hw); |
123 | 147 | ||
124 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | 148 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, |
125 | dice_rate_constraint, dice, | 149 | dice_rate_constraint, substream, |
126 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); | 150 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); |
127 | if (err < 0) | 151 | if (err < 0) |
128 | goto end; | 152 | goto end; |
129 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | 153 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, |
130 | dice_channels_constraint, dice, | 154 | dice_channels_constraint, substream, |
131 | SNDRV_PCM_HW_PARAM_RATE, -1); | 155 | SNDRV_PCM_HW_PARAM_RATE, -1); |
132 | if (err < 0) | 156 | if (err < 0) |
133 | goto end; | 157 | goto end; |
134 | 158 | ||
135 | err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); | 159 | err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); |
136 | end: | 160 | end: |
137 | return err; | 161 | return err; |
138 | } | 162 | } |
@@ -172,10 +196,12 @@ static int pcm_open(struct snd_pcm_substream *substream) | |||
172 | } | 196 | } |
173 | 197 | ||
174 | /* | 198 | /* |
175 | * When source of clock is not internal, available sampling rate is | 199 | * When source of clock is not internal or any PCM streams are running, |
176 | * limited at current sampling rate. | 200 | * available sampling rate is limited at current sampling rate. |
177 | */ | 201 | */ |
178 | if (!internal) { | 202 | if (!internal || |
203 | amdtp_stream_pcm_running(&dice->tx_stream) || | ||
204 | amdtp_stream_pcm_running(&dice->rx_stream)) { | ||
179 | err = snd_dice_transaction_get_rate(dice, &rate); | 205 | err = snd_dice_transaction_get_rate(dice, &rate); |
180 | if (err < 0) | 206 | if (err < 0) |
181 | goto err_locked; | 207 | goto err_locked; |
@@ -200,10 +226,34 @@ static int pcm_close(struct snd_pcm_substream *substream) | |||
200 | return 0; | 226 | return 0; |
201 | } | 227 | } |
202 | 228 | ||
229 | static int capture_hw_params(struct snd_pcm_substream *substream, | ||
230 | struct snd_pcm_hw_params *hw_params) | ||
231 | { | ||
232 | struct snd_dice *dice = substream->private_data; | ||
233 | |||
234 | if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { | ||
235 | mutex_lock(&dice->mutex); | ||
236 | dice->substreams_counter++; | ||
237 | mutex_unlock(&dice->mutex); | ||
238 | } | ||
239 | |||
240 | amdtp_stream_set_pcm_format(&dice->tx_stream, | ||
241 | params_format(hw_params)); | ||
242 | |||
243 | return snd_pcm_lib_alloc_vmalloc_buffer(substream, | ||
244 | params_buffer_bytes(hw_params)); | ||
245 | } | ||
203 | static int playback_hw_params(struct snd_pcm_substream *substream, | 246 | static int playback_hw_params(struct snd_pcm_substream *substream, |
204 | struct snd_pcm_hw_params *hw_params) | 247 | struct snd_pcm_hw_params *hw_params) |
205 | { | 248 | { |
206 | struct snd_dice *dice = substream->private_data; | 249 | struct snd_dice *dice = substream->private_data; |
250 | |||
251 | if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { | ||
252 | mutex_lock(&dice->mutex); | ||
253 | dice->substreams_counter++; | ||
254 | mutex_unlock(&dice->mutex); | ||
255 | } | ||
256 | |||
207 | amdtp_stream_set_pcm_format(&dice->rx_stream, | 257 | amdtp_stream_set_pcm_format(&dice->rx_stream, |
208 | params_format(hw_params)); | 258 | params_format(hw_params)); |
209 | 259 | ||
@@ -211,17 +261,51 @@ static int playback_hw_params(struct snd_pcm_substream *substream, | |||
211 | params_buffer_bytes(hw_params)); | 261 | params_buffer_bytes(hw_params)); |
212 | } | 262 | } |
213 | 263 | ||
264 | static int capture_hw_free(struct snd_pcm_substream *substream) | ||
265 | { | ||
266 | struct snd_dice *dice = substream->private_data; | ||
267 | |||
268 | mutex_lock(&dice->mutex); | ||
269 | |||
270 | if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) | ||
271 | dice->substreams_counter--; | ||
272 | |||
273 | snd_dice_stream_stop_duplex(dice); | ||
274 | |||
275 | mutex_unlock(&dice->mutex); | ||
276 | |||
277 | return snd_pcm_lib_free_vmalloc_buffer(substream); | ||
278 | } | ||
279 | |||
214 | static int playback_hw_free(struct snd_pcm_substream *substream) | 280 | static int playback_hw_free(struct snd_pcm_substream *substream) |
215 | { | 281 | { |
216 | struct snd_dice *dice = substream->private_data; | 282 | struct snd_dice *dice = substream->private_data; |
217 | 283 | ||
218 | mutex_lock(&dice->mutex); | 284 | mutex_lock(&dice->mutex); |
285 | |||
286 | if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) | ||
287 | dice->substreams_counter--; | ||
288 | |||
219 | snd_dice_stream_stop_duplex(dice); | 289 | snd_dice_stream_stop_duplex(dice); |
290 | |||
220 | mutex_unlock(&dice->mutex); | 291 | mutex_unlock(&dice->mutex); |
221 | 292 | ||
222 | return snd_pcm_lib_free_vmalloc_buffer(substream); | 293 | return snd_pcm_lib_free_vmalloc_buffer(substream); |
223 | } | 294 | } |
224 | 295 | ||
296 | static int capture_prepare(struct snd_pcm_substream *substream) | ||
297 | { | ||
298 | struct snd_dice *dice = substream->private_data; | ||
299 | int err; | ||
300 | |||
301 | mutex_lock(&dice->mutex); | ||
302 | err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); | ||
303 | mutex_unlock(&dice->mutex); | ||
304 | if (err >= 0) | ||
305 | amdtp_stream_pcm_prepare(&dice->tx_stream); | ||
306 | |||
307 | return 0; | ||
308 | } | ||
225 | static int playback_prepare(struct snd_pcm_substream *substream) | 309 | static int playback_prepare(struct snd_pcm_substream *substream) |
226 | { | 310 | { |
227 | struct snd_dice *dice = substream->private_data; | 311 | struct snd_dice *dice = substream->private_data; |
@@ -236,6 +320,23 @@ static int playback_prepare(struct snd_pcm_substream *substream) | |||
236 | return err; | 320 | return err; |
237 | } | 321 | } |
238 | 322 | ||
323 | static int capture_trigger(struct snd_pcm_substream *substream, int cmd) | ||
324 | { | ||
325 | struct snd_dice *dice = substream->private_data; | ||
326 | |||
327 | switch (cmd) { | ||
328 | case SNDRV_PCM_TRIGGER_START: | ||
329 | amdtp_stream_pcm_trigger(&dice->tx_stream, substream); | ||
330 | break; | ||
331 | case SNDRV_PCM_TRIGGER_STOP: | ||
332 | amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); | ||
333 | break; | ||
334 | default: | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | |||
338 | return 0; | ||
339 | } | ||
239 | static int playback_trigger(struct snd_pcm_substream *substream, int cmd) | 340 | static int playback_trigger(struct snd_pcm_substream *substream, int cmd) |
240 | { | 341 | { |
241 | struct snd_dice *dice = substream->private_data; | 342 | struct snd_dice *dice = substream->private_data; |
@@ -254,6 +355,12 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
254 | return 0; | 355 | return 0; |
255 | } | 356 | } |
256 | 357 | ||
358 | static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) | ||
359 | { | ||
360 | struct snd_dice *dice = substream->private_data; | ||
361 | |||
362 | return amdtp_stream_pcm_pointer(&dice->tx_stream); | ||
363 | } | ||
257 | static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) | 364 | static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) |
258 | { | 365 | { |
259 | struct snd_dice *dice = substream->private_data; | 366 | struct snd_dice *dice = substream->private_data; |
@@ -263,6 +370,18 @@ static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) | |||
263 | 370 | ||
264 | int snd_dice_create_pcm(struct snd_dice *dice) | 371 | int snd_dice_create_pcm(struct snd_dice *dice) |
265 | { | 372 | { |
373 | static struct snd_pcm_ops capture_ops = { | ||
374 | .open = pcm_open, | ||
375 | .close = pcm_close, | ||
376 | .ioctl = snd_pcm_lib_ioctl, | ||
377 | .hw_params = capture_hw_params, | ||
378 | .hw_free = capture_hw_free, | ||
379 | .prepare = capture_prepare, | ||
380 | .trigger = capture_trigger, | ||
381 | .pointer = capture_pointer, | ||
382 | .page = snd_pcm_lib_get_vmalloc_page, | ||
383 | .mmap = snd_pcm_lib_mmap_vmalloc, | ||
384 | }; | ||
266 | static struct snd_pcm_ops playback_ops = { | 385 | static struct snd_pcm_ops playback_ops = { |
267 | .open = pcm_open, | 386 | .open = pcm_open, |
268 | .close = pcm_close, | 387 | .close = pcm_close, |
@@ -276,14 +395,28 @@ int snd_dice_create_pcm(struct snd_dice *dice) | |||
276 | .mmap = snd_pcm_lib_mmap_vmalloc, | 395 | .mmap = snd_pcm_lib_mmap_vmalloc, |
277 | }; | 396 | }; |
278 | struct snd_pcm *pcm; | 397 | struct snd_pcm *pcm; |
398 | unsigned int i, capture, playback; | ||
279 | int err; | 399 | int err; |
280 | 400 | ||
281 | err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); | 401 | capture = playback = 0; |
402 | for (i = 0; i < 3; i++) { | ||
403 | if (dice->tx_channels[i] > 0) | ||
404 | capture = 1; | ||
405 | if (dice->rx_channels[i] > 0) | ||
406 | playback = 1; | ||
407 | } | ||
408 | |||
409 | err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); | ||
282 | if (err < 0) | 410 | if (err < 0) |
283 | return err; | 411 | return err; |
284 | pcm->private_data = dice; | 412 | pcm->private_data = dice; |
285 | strcpy(pcm->name, dice->card->shortname); | 413 | strcpy(pcm->name, dice->card->shortname); |
286 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); | 414 | |
415 | if (capture > 0) | ||
416 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); | ||
417 | |||
418 | if (playback > 0) | ||
419 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); | ||
287 | 420 | ||
288 | return 0; | 421 | return 0; |
289 | } | 422 | } |