diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2011-12-04 16:07:01 -0500 |
---|---|---|
committer | Clemens Ladisch <clemens@ladisch.de> | 2013-10-20 16:07:57 -0400 |
commit | 4edeb831f32d17fba056eb752f7afc26a19674a0 (patch) | |
tree | 5573fb9615f009890bbbc6dce1e1ae713b826d74 /sound/firewire | |
parent | 15a75c8bed591dd23a3d221f5ccd91843c109670 (diff) |
ALSA: dice: dynamic sample rate selection
Instead of relying of some control panel application to configure some
fixed sample rate, allow applications to set it automatically.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Diffstat (limited to 'sound/firewire')
-rw-r--r-- | sound/firewire/dice.c | 137 |
1 files changed, 102 insertions, 35 deletions
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index e6bba6d32bd8..61dd00c4fae3 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c | |||
@@ -70,6 +70,17 @@ static const unsigned int dice_rates[] = { | |||
70 | [6] = 192000, | 70 | [6] = 192000, |
71 | }; | 71 | }; |
72 | 72 | ||
73 | static unsigned int rate_to_index(unsigned int rate) | ||
74 | { | ||
75 | unsigned int i; | ||
76 | |||
77 | for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) | ||
78 | if (dice_rates[i] == rate) | ||
79 | return i; | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
73 | static unsigned int rate_index_to_mode(unsigned int rate_index) | 84 | static unsigned int rate_index_to_mode(unsigned int rate_index) |
74 | { | 85 | { |
75 | return ((int)rate_index - 1) / 2; | 86 | return ((int)rate_index - 1) / 2; |
@@ -302,6 +313,59 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, | |||
302 | wake_up(&dice->hwdep_wait); | 313 | wake_up(&dice->hwdep_wait); |
303 | } | 314 | } |
304 | 315 | ||
316 | static int dice_rate_constraint(struct snd_pcm_hw_params *params, | ||
317 | struct snd_pcm_hw_rule *rule) | ||
318 | { | ||
319 | struct dice *dice = rule->private; | ||
320 | const struct snd_interval *channels = | ||
321 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
322 | struct snd_interval *rate = | ||
323 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | ||
324 | struct snd_interval allowed_rates = { | ||
325 | .min = UINT_MAX, .max = 0, .integer = 1 | ||
326 | }; | ||
327 | unsigned int i, mode; | ||
328 | |||
329 | for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) { | ||
330 | mode = rate_index_to_mode(i); | ||
331 | if ((dice->clock_caps & (1 << i)) && | ||
332 | snd_interval_test(channels, dice->rx_channels[mode])) { | ||
333 | allowed_rates.min = min(allowed_rates.min, | ||
334 | dice_rates[i]); | ||
335 | allowed_rates.max = max(allowed_rates.max, | ||
336 | dice_rates[i]); | ||
337 | } | ||
338 | } | ||
339 | |||
340 | return snd_interval_refine(rate, &allowed_rates); | ||
341 | } | ||
342 | |||
343 | static int dice_channels_constraint(struct snd_pcm_hw_params *params, | ||
344 | struct snd_pcm_hw_rule *rule) | ||
345 | { | ||
346 | struct dice *dice = rule->private; | ||
347 | const struct snd_interval *rate = | ||
348 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); | ||
349 | struct snd_interval *channels = | ||
350 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
351 | struct snd_interval allowed_channels = { | ||
352 | .min = UINT_MAX, .max = 0, .integer = 1 | ||
353 | }; | ||
354 | unsigned int i, mode; | ||
355 | |||
356 | for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) | ||
357 | if ((dice->clock_caps & (1 << i)) && | ||
358 | snd_interval_test(rate, dice_rates[i])) { | ||
359 | mode = rate_index_to_mode(i); | ||
360 | allowed_channels.min = min(allowed_channels.min, | ||
361 | dice->rx_channels[mode]); | ||
362 | allowed_channels.max = max(allowed_channels.max, | ||
363 | dice->rx_channels[mode]); | ||
364 | } | ||
365 | |||
366 | return snd_interval_refine(channels, &allowed_channels); | ||
367 | } | ||
368 | |||
305 | static int dice_open(struct snd_pcm_substream *substream) | 369 | static int dice_open(struct snd_pcm_substream *substream) |
306 | { | 370 | { |
307 | static const struct snd_pcm_hardware hardware = { | 371 | static const struct snd_pcm_hardware hardware = { |
@@ -311,6 +375,8 @@ static int dice_open(struct snd_pcm_substream *substream) | |||
311 | SNDRV_PCM_INFO_INTERLEAVED | | 375 | SNDRV_PCM_INFO_INTERLEAVED | |
312 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | 376 | SNDRV_PCM_INFO_BLOCK_TRANSFER, |
313 | .formats = AMDTP_OUT_PCM_FORMAT_BITS, | 377 | .formats = AMDTP_OUT_PCM_FORMAT_BITS, |
378 | .channels_min = UINT_MAX, | ||
379 | .channels_max = 0, | ||
314 | .buffer_bytes_max = 16 * 1024 * 1024, | 380 | .buffer_bytes_max = 16 * 1024 * 1024, |
315 | .period_bytes_min = 1, | 381 | .period_bytes_min = 1, |
316 | .period_bytes_max = UINT_MAX, | 382 | .period_bytes_max = UINT_MAX, |
@@ -319,53 +385,46 @@ static int dice_open(struct snd_pcm_substream *substream) | |||
319 | }; | 385 | }; |
320 | struct dice *dice = substream->private_data; | 386 | struct dice *dice = substream->private_data; |
321 | struct snd_pcm_runtime *runtime = substream->runtime; | 387 | struct snd_pcm_runtime *runtime = substream->runtime; |
322 | __be32 clock_sel, data[2]; | 388 | unsigned int i; |
323 | unsigned int rate_index, number_audio, number_midi; | ||
324 | int err; | 389 | int err; |
325 | 390 | ||
326 | err = dice_try_lock(dice); | 391 | err = dice_try_lock(dice); |
327 | if (err < 0) | 392 | if (err < 0) |
328 | goto error; | 393 | goto error; |
329 | 394 | ||
330 | err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, | ||
331 | global_address(dice, GLOBAL_CLOCK_SELECT), | ||
332 | &clock_sel, 4, 0); | ||
333 | if (err < 0) | ||
334 | goto err_lock; | ||
335 | rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) | ||
336 | >> CLOCK_RATE_SHIFT; | ||
337 | if (rate_index >= ARRAY_SIZE(dice_rates)) { | ||
338 | err = -ENXIO; | ||
339 | goto err_lock; | ||
340 | } | ||
341 | |||
342 | err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, | ||
343 | rx_address(dice, RX_NUMBER_AUDIO), | ||
344 | data, 2 * 4, 0); | ||
345 | if (err < 0) | ||
346 | goto err_lock; | ||
347 | number_audio = be32_to_cpu(data[0]); | ||
348 | number_midi = be32_to_cpu(data[1]); | ||
349 | |||
350 | runtime->hw = hardware; | 395 | runtime->hw = hardware; |
351 | 396 | ||
352 | runtime->hw.rates = snd_pcm_rate_to_rate_bit(dice_rates[rate_index]); | 397 | for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) |
398 | if (dice->clock_caps & (1 << i)) | ||
399 | runtime->hw.rates |= | ||
400 | snd_pcm_rate_to_rate_bit(dice_rates[i]); | ||
353 | snd_pcm_limit_hw_rates(runtime); | 401 | snd_pcm_limit_hw_rates(runtime); |
354 | 402 | ||
355 | runtime->hw.channels_min = number_audio; | 403 | for (i = 0; i < 3; ++i) |
356 | runtime->hw.channels_max = number_audio; | 404 | if (dice->rx_channels[i]) { |
405 | runtime->hw.channels_min = min(runtime->hw.channels_min, | ||
406 | dice->rx_channels[i]); | ||
407 | runtime->hw.channels_max = max(runtime->hw.channels_max, | ||
408 | dice->rx_channels[i]); | ||
409 | } | ||
357 | 410 | ||
358 | amdtp_out_stream_set_parameters(&dice->stream, dice_rates[rate_index], | 411 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, |
359 | number_audio, number_midi); | 412 | dice_rate_constraint, dice, |
413 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); | ||
414 | if (err < 0) | ||
415 | goto err_lock; | ||
416 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | ||
417 | dice_channels_constraint, dice, | ||
418 | SNDRV_PCM_HW_PARAM_RATE, -1); | ||
419 | if (err < 0) | ||
420 | goto err_lock; | ||
360 | 421 | ||
361 | err = snd_pcm_hw_constraint_step(runtime, 0, | 422 | err = snd_pcm_hw_constraint_step(runtime, 0, |
362 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | 423 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); |
363 | amdtp_syt_intervals[rate_index]); | ||
364 | if (err < 0) | 424 | if (err < 0) |
365 | goto err_lock; | 425 | goto err_lock; |
366 | err = snd_pcm_hw_constraint_step(runtime, 0, | 426 | err = snd_pcm_hw_constraint_step(runtime, 0, |
367 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, | 427 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); |
368 | amdtp_syt_intervals[rate_index]); | ||
369 | if (err < 0) | 428 | if (err < 0) |
370 | goto err_lock; | 429 | goto err_lock; |
371 | 430 | ||
@@ -502,6 +561,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, | |||
502 | struct snd_pcm_hw_params *hw_params) | 561 | struct snd_pcm_hw_params *hw_params) |
503 | { | 562 | { |
504 | struct dice *dice = substream->private_data; | 563 | struct dice *dice = substream->private_data; |
564 | unsigned int rate_index, mode; | ||
505 | int err; | 565 | int err; |
506 | 566 | ||
507 | mutex_lock(&dice->mutex); | 567 | mutex_lock(&dice->mutex); |
@@ -511,15 +571,22 @@ static int dice_hw_params(struct snd_pcm_substream *substream, | |||
511 | err = snd_pcm_lib_alloc_vmalloc_buffer(substream, | 571 | err = snd_pcm_lib_alloc_vmalloc_buffer(substream, |
512 | params_buffer_bytes(hw_params)); | 572 | params_buffer_bytes(hw_params)); |
513 | if (err < 0) | 573 | if (err < 0) |
514 | goto error; | 574 | return err; |
515 | 575 | ||
576 | rate_index = rate_to_index(params_rate(hw_params)); | ||
577 | err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); | ||
578 | if (err < 0) | ||
579 | return err; | ||
580 | |||
581 | mode = rate_index_to_mode(rate_index); | ||
582 | amdtp_out_stream_set_parameters(&dice->stream, | ||
583 | params_rate(hw_params), | ||
584 | params_channels(hw_params), | ||
585 | dice->rx_midi_ports[mode]); | ||
516 | amdtp_out_stream_set_pcm_format(&dice->stream, | 586 | amdtp_out_stream_set_pcm_format(&dice->stream, |
517 | params_format(hw_params)); | 587 | params_format(hw_params)); |
518 | 588 | ||
519 | return 0; | 589 | return 0; |
520 | |||
521 | error: | ||
522 | return err; | ||
523 | } | 590 | } |
524 | 591 | ||
525 | static int dice_hw_free(struct snd_pcm_substream *substream) | 592 | static int dice_hw_free(struct snd_pcm_substream *substream) |