diff options
| -rw-r--r-- | sound/usb/usbaudio.c | 100 |
1 files changed, 62 insertions, 38 deletions
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 8818918c4237..99dae024b640 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c | |||
| @@ -41,7 +41,6 @@ | |||
| 41 | #include <sound/driver.h> | 41 | #include <sound/driver.h> |
| 42 | #include <linux/bitops.h> | 42 | #include <linux/bitops.h> |
| 43 | #include <linux/init.h> | 43 | #include <linux/init.h> |
| 44 | #include <linux/interrupt.h> | ||
| 45 | #include <linux/list.h> | 44 | #include <linux/list.h> |
| 46 | #include <linux/slab.h> | 45 | #include <linux/slab.h> |
| 47 | #include <linux/string.h> | 46 | #include <linux/string.h> |
| @@ -185,7 +184,6 @@ struct snd_usb_substream { | |||
| 185 | unsigned int num_formats; /* number of supported audio formats (list) */ | 184 | unsigned int num_formats; /* number of supported audio formats (list) */ |
| 186 | struct list_head fmt_list; /* format list */ | 185 | struct list_head fmt_list; /* format list */ |
| 187 | spinlock_t lock; | 186 | spinlock_t lock; |
| 188 | struct tasklet_struct start_period_elapsed; /* for start trigger */ | ||
| 189 | 187 | ||
| 190 | struct snd_urb_ops ops; /* callbacks (must be filled at init) */ | 188 | struct snd_urb_ops ops; /* callbacks (must be filled at init) */ |
| 191 | }; | 189 | }; |
| @@ -480,6 +478,28 @@ static int retire_playback_sync_urb_hs(snd_usb_substream_t *subs, | |||
| 480 | } | 478 | } |
| 481 | 479 | ||
| 482 | /* | 480 | /* |
| 481 | * Prepare urb for streaming before playback starts. | ||
| 482 | * | ||
| 483 | * We don't care about (or have) any data, so we just send a transfer delimiter. | ||
| 484 | */ | ||
| 485 | static int prepare_startup_playback_urb(snd_usb_substream_t *subs, | ||
| 486 | snd_pcm_runtime_t *runtime, | ||
| 487 | struct urb *urb) | ||
| 488 | { | ||
| 489 | unsigned int i; | ||
| 490 | snd_urb_ctx_t *ctx = urb->context; | ||
| 491 | |||
| 492 | urb->dev = ctx->subs->dev; | ||
| 493 | urb->number_of_packets = subs->packs_per_ms; | ||
| 494 | for (i = 0; i < subs->packs_per_ms; ++i) { | ||
| 495 | urb->iso_frame_desc[i].offset = 0; | ||
| 496 | urb->iso_frame_desc[i].length = 0; | ||
| 497 | } | ||
| 498 | urb->transfer_buffer_length = 0; | ||
| 499 | return 0; | ||
| 500 | } | ||
| 501 | |||
| 502 | /* | ||
| 483 | * prepare urb for playback data pipe | 503 | * prepare urb for playback data pipe |
| 484 | * | 504 | * |
| 485 | * Since a URB can handle only a single linear buffer, we must use double | 505 | * Since a URB can handle only a single linear buffer, we must use double |
| @@ -568,12 +588,8 @@ static int prepare_playback_urb(snd_usb_substream_t *subs, | |||
| 568 | subs->hwptr_done -= runtime->buffer_size; | 588 | subs->hwptr_done -= runtime->buffer_size; |
| 569 | spin_unlock_irqrestore(&subs->lock, flags); | 589 | spin_unlock_irqrestore(&subs->lock, flags); |
| 570 | urb->transfer_buffer_length = offs * stride; | 590 | urb->transfer_buffer_length = offs * stride; |
| 571 | if (period_elapsed) { | 591 | if (period_elapsed) |
| 572 | if (likely(subs->running)) | 592 | snd_pcm_period_elapsed(subs->pcm_substream); |
| 573 | snd_pcm_period_elapsed(subs->pcm_substream); | ||
| 574 | else | ||
| 575 | tasklet_hi_schedule(&subs->start_period_elapsed); | ||
| 576 | } | ||
| 577 | return 0; | 593 | return 0; |
| 578 | } | 594 | } |
| 579 | 595 | ||
| @@ -588,22 +604,12 @@ static int retire_playback_urb(snd_usb_substream_t *subs, | |||
| 588 | return 0; | 604 | return 0; |
| 589 | } | 605 | } |
| 590 | 606 | ||
| 591 | /* | ||
| 592 | * Delay the snd_pcm_period_elapsed() call until after the start trigger | ||
| 593 | * callback so that we're not longer in the substream's lock. | ||
| 594 | */ | ||
| 595 | static void start_period_elapsed(unsigned long data) | ||
| 596 | { | ||
| 597 | snd_usb_substream_t *subs = (snd_usb_substream_t *)data; | ||
| 598 | snd_pcm_period_elapsed(subs->pcm_substream); | ||
| 599 | } | ||
| 600 | |||
| 601 | 607 | ||
| 602 | /* | 608 | /* |
| 603 | */ | 609 | */ |
| 604 | static struct snd_urb_ops audio_urb_ops[2] = { | 610 | static struct snd_urb_ops audio_urb_ops[2] = { |
| 605 | { | 611 | { |
| 606 | .prepare = prepare_playback_urb, | 612 | .prepare = prepare_startup_playback_urb, |
| 607 | .retire = retire_playback_urb, | 613 | .retire = retire_playback_urb, |
| 608 | .prepare_sync = prepare_playback_sync_urb, | 614 | .prepare_sync = prepare_playback_sync_urb, |
| 609 | .retire_sync = retire_playback_sync_urb, | 615 | .retire_sync = retire_playback_sync_urb, |
| @@ -618,7 +624,7 @@ static struct snd_urb_ops audio_urb_ops[2] = { | |||
| 618 | 624 | ||
| 619 | static struct snd_urb_ops audio_urb_ops_high_speed[2] = { | 625 | static struct snd_urb_ops audio_urb_ops_high_speed[2] = { |
| 620 | { | 626 | { |
| 621 | .prepare = prepare_playback_urb, | 627 | .prepare = prepare_startup_playback_urb, |
| 622 | .retire = retire_playback_urb, | 628 | .retire = retire_playback_urb, |
| 623 | .prepare_sync = prepare_playback_sync_urb_hs, | 629 | .prepare_sync = prepare_playback_sync_urb_hs, |
| 624 | .retire_sync = retire_playback_sync_urb_hs, | 630 | .retire_sync = retire_playback_sync_urb_hs, |
| @@ -863,25 +869,40 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(snd_pcm_substream_t *substream) | |||
| 863 | 869 | ||
| 864 | 870 | ||
| 865 | /* | 871 | /* |
| 866 | * start/stop substream | 872 | * start/stop playback substream |
| 867 | */ | 873 | */ |
| 868 | static int snd_usb_pcm_trigger(snd_pcm_substream_t *substream, int cmd) | 874 | static int snd_usb_pcm_playback_trigger(snd_pcm_substream_t *substream, |
| 875 | int cmd) | ||
| 869 | { | 876 | { |
| 870 | snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data; | 877 | snd_usb_substream_t *subs = substream->runtime->private_data; |
| 871 | int err; | ||
| 872 | 878 | ||
| 873 | switch (cmd) { | 879 | switch (cmd) { |
| 874 | case SNDRV_PCM_TRIGGER_START: | 880 | case SNDRV_PCM_TRIGGER_START: |
| 875 | err = start_urbs(subs, substream->runtime); | 881 | subs->ops.prepare = prepare_playback_urb; |
| 876 | break; | 882 | return 0; |
| 877 | case SNDRV_PCM_TRIGGER_STOP: | 883 | case SNDRV_PCM_TRIGGER_STOP: |
| 878 | err = deactivate_urbs(subs, 0, 0); | 884 | return deactivate_urbs(subs, 0, 0); |
| 879 | break; | ||
| 880 | default: | 885 | default: |
| 881 | err = -EINVAL; | 886 | return -EINVAL; |
| 882 | break; | 887 | } |
| 888 | } | ||
| 889 | |||
| 890 | /* | ||
| 891 | * start/stop capture substream | ||
| 892 | */ | ||
| 893 | static int snd_usb_pcm_capture_trigger(snd_pcm_substream_t *substream, | ||
| 894 | int cmd) | ||
| 895 | { | ||
| 896 | snd_usb_substream_t *subs = substream->runtime->private_data; | ||
| 897 | |||
| 898 | switch (cmd) { | ||
| 899 | case SNDRV_PCM_TRIGGER_START: | ||
| 900 | return start_urbs(subs, substream->runtime); | ||
| 901 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 902 | return deactivate_urbs(subs, 0, 0); | ||
| 903 | default: | ||
| 904 | return -EINVAL; | ||
| 883 | } | 905 | } |
| 884 | return err < 0 ? err : 0; | ||
| 885 | } | 906 | } |
| 886 | 907 | ||
| 887 | 908 | ||
| @@ -1413,7 +1434,7 @@ static int snd_usb_hw_free(snd_pcm_substream_t *substream) | |||
| 1413 | static int snd_usb_pcm_prepare(snd_pcm_substream_t *substream) | 1434 | static int snd_usb_pcm_prepare(snd_pcm_substream_t *substream) |
| 1414 | { | 1435 | { |
| 1415 | snd_pcm_runtime_t *runtime = substream->runtime; | 1436 | snd_pcm_runtime_t *runtime = substream->runtime; |
| 1416 | snd_usb_substream_t *subs = (snd_usb_substream_t *)runtime->private_data; | 1437 | snd_usb_substream_t *subs = runtime->private_data; |
| 1417 | 1438 | ||
| 1418 | if (! subs->cur_audiofmt) { | 1439 | if (! subs->cur_audiofmt) { |
| 1419 | snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); | 1440 | snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); |
| @@ -1433,7 +1454,13 @@ static int snd_usb_pcm_prepare(snd_pcm_substream_t *substream) | |||
| 1433 | deactivate_urbs(subs, 0, 1); | 1454 | deactivate_urbs(subs, 0, 1); |
| 1434 | wait_clear_urbs(subs); | 1455 | wait_clear_urbs(subs); |
| 1435 | 1456 | ||
| 1436 | return 0; | 1457 | /* for playback, submit the URBs now; otherwise, the first hwptr_done |
| 1458 | * updates for all URBs would happen at the same time when starting */ | ||
| 1459 | if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 1460 | subs->ops.prepare = prepare_startup_playback_urb; | ||
| 1461 | return start_urbs(subs, runtime); | ||
| 1462 | } else | ||
| 1463 | return 0; | ||
| 1437 | } | 1464 | } |
| 1438 | 1465 | ||
| 1439 | static snd_pcm_hardware_t snd_usb_playback = | 1466 | static snd_pcm_hardware_t snd_usb_playback = |
| @@ -1847,7 +1874,7 @@ static snd_pcm_ops_t snd_usb_playback_ops = { | |||
| 1847 | .hw_params = snd_usb_hw_params, | 1874 | .hw_params = snd_usb_hw_params, |
| 1848 | .hw_free = snd_usb_hw_free, | 1875 | .hw_free = snd_usb_hw_free, |
| 1849 | .prepare = snd_usb_pcm_prepare, | 1876 | .prepare = snd_usb_pcm_prepare, |
| 1850 | .trigger = snd_usb_pcm_trigger, | 1877 | .trigger = snd_usb_pcm_playback_trigger, |
| 1851 | .pointer = snd_usb_pcm_pointer, | 1878 | .pointer = snd_usb_pcm_pointer, |
| 1852 | .page = snd_pcm_get_vmalloc_page, | 1879 | .page = snd_pcm_get_vmalloc_page, |
| 1853 | }; | 1880 | }; |
| @@ -1859,7 +1886,7 @@ static snd_pcm_ops_t snd_usb_capture_ops = { | |||
| 1859 | .hw_params = snd_usb_hw_params, | 1886 | .hw_params = snd_usb_hw_params, |
| 1860 | .hw_free = snd_usb_hw_free, | 1887 | .hw_free = snd_usb_hw_free, |
| 1861 | .prepare = snd_usb_pcm_prepare, | 1888 | .prepare = snd_usb_pcm_prepare, |
| 1862 | .trigger = snd_usb_pcm_trigger, | 1889 | .trigger = snd_usb_pcm_capture_trigger, |
| 1863 | .pointer = snd_usb_pcm_pointer, | 1890 | .pointer = snd_usb_pcm_pointer, |
| 1864 | .page = snd_pcm_get_vmalloc_page, | 1891 | .page = snd_pcm_get_vmalloc_page, |
| 1865 | }; | 1892 | }; |
| @@ -2078,9 +2105,6 @@ static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat | |||
| 2078 | 2105 | ||
| 2079 | INIT_LIST_HEAD(&subs->fmt_list); | 2106 | INIT_LIST_HEAD(&subs->fmt_list); |
| 2080 | spin_lock_init(&subs->lock); | 2107 | spin_lock_init(&subs->lock); |
| 2081 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 2082 | tasklet_init(&subs->start_period_elapsed, start_period_elapsed, | ||
| 2083 | (unsigned long)subs); | ||
| 2084 | 2108 | ||
| 2085 | subs->stream = as; | 2109 | subs->stream = as; |
| 2086 | subs->direction = stream; | 2110 | subs->direction = stream; |
