diff options
author | Takashi Iwai <tiwai@suse.de> | 2010-10-20 06:22:11 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2010-10-20 06:22:11 -0400 |
commit | f3305da35a26def93b476051f4d7b6bc791e561e (patch) | |
tree | 98dd3c78737a6cee8613b79772c000f0098e4f4a /sound/drivers | |
parent | 83fc3bc09518d42e8f5073e2a65884701dfadf19 (diff) | |
parent | 5de9e45fcfccdf8151a82fc1a521e7042cbe482a (diff) |
Merge remote branch 'alsa/devel' into topic/misc
Diffstat (limited to 'sound/drivers')
-rw-r--r-- | sound/drivers/aloop.c | 139 |
1 files changed, 123 insertions, 16 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 838ad86311b8..12b44b0b6777 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <sound/core.h> | 39 | #include <sound/core.h> |
40 | #include <sound/control.h> | 40 | #include <sound/control.h> |
41 | #include <sound/pcm.h> | 41 | #include <sound/pcm.h> |
42 | #include <sound/info.h> | ||
42 | #include <sound/initval.h> | 43 | #include <sound/initval.h> |
43 | 44 | ||
44 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); | 45 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
@@ -76,6 +77,7 @@ struct loopback_cable { | |||
76 | /* flags */ | 77 | /* flags */ |
77 | unsigned int valid; | 78 | unsigned int valid; |
78 | unsigned int running; | 79 | unsigned int running; |
80 | unsigned int pause; | ||
79 | }; | 81 | }; |
80 | 82 | ||
81 | struct loopback_setup { | 83 | struct loopback_setup { |
@@ -184,6 +186,7 @@ static void loopback_timer_start(struct loopback_pcm *dpcm) | |||
184 | static inline void loopback_timer_stop(struct loopback_pcm *dpcm) | 186 | static inline void loopback_timer_stop(struct loopback_pcm *dpcm) |
185 | { | 187 | { |
186 | del_timer(&dpcm->timer); | 188 | del_timer(&dpcm->timer); |
189 | dpcm->timer.expires = 0; | ||
187 | } | 190 | } |
188 | 191 | ||
189 | #define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) | 192 | #define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) |
@@ -252,7 +255,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
252 | struct snd_pcm_runtime *runtime = substream->runtime; | 255 | struct snd_pcm_runtime *runtime = substream->runtime; |
253 | struct loopback_pcm *dpcm = runtime->private_data; | 256 | struct loopback_pcm *dpcm = runtime->private_data; |
254 | struct loopback_cable *cable = dpcm->cable; | 257 | struct loopback_cable *cable = dpcm->cable; |
255 | int err; | 258 | int err, stream = 1 << substream->stream; |
256 | 259 | ||
257 | switch (cmd) { | 260 | switch (cmd) { |
258 | case SNDRV_PCM_TRIGGER_START: | 261 | case SNDRV_PCM_TRIGGER_START: |
@@ -261,17 +264,36 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
261 | return err; | 264 | return err; |
262 | dpcm->last_jiffies = jiffies; | 265 | dpcm->last_jiffies = jiffies; |
263 | dpcm->pcm_rate_shift = 0; | 266 | dpcm->pcm_rate_shift = 0; |
267 | spin_lock(&cable->lock); | ||
268 | cable->running |= stream; | ||
269 | cable->pause &= ~stream; | ||
270 | spin_unlock(&cable->lock); | ||
264 | loopback_timer_start(dpcm); | 271 | loopback_timer_start(dpcm); |
265 | cable->running |= (1 << substream->stream); | ||
266 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 272 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
267 | loopback_active_notify(dpcm); | 273 | loopback_active_notify(dpcm); |
268 | break; | 274 | break; |
269 | case SNDRV_PCM_TRIGGER_STOP: | 275 | case SNDRV_PCM_TRIGGER_STOP: |
270 | cable->running &= ~(1 << substream->stream); | 276 | spin_lock(&cable->lock); |
277 | cable->running &= ~stream; | ||
278 | cable->pause &= ~stream; | ||
279 | spin_unlock(&cable->lock); | ||
271 | loopback_timer_stop(dpcm); | 280 | loopback_timer_stop(dpcm); |
272 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 281 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
273 | loopback_active_notify(dpcm); | 282 | loopback_active_notify(dpcm); |
274 | break; | 283 | break; |
284 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
285 | spin_lock(&cable->lock); | ||
286 | cable->pause |= stream; | ||
287 | spin_unlock(&cable->lock); | ||
288 | loopback_timer_stop(dpcm); | ||
289 | break; | ||
290 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
291 | spin_lock(&cable->lock); | ||
292 | dpcm->last_jiffies = jiffies; | ||
293 | cable->pause &= ~stream; | ||
294 | spin_unlock(&cable->lock); | ||
295 | loopback_timer_start(dpcm); | ||
296 | break; | ||
275 | default: | 297 | default: |
276 | return -EINVAL; | 298 | return -EINVAL; |
277 | } | 299 | } |
@@ -452,28 +474,30 @@ static void loopback_bytepos_update(struct loopback_pcm *dpcm, | |||
452 | } | 474 | } |
453 | } | 475 | } |
454 | 476 | ||
455 | static void loopback_pos_update(struct loopback_cable *cable) | 477 | static unsigned int loopback_pos_update(struct loopback_cable *cable) |
456 | { | 478 | { |
457 | struct loopback_pcm *dpcm_play = | 479 | struct loopback_pcm *dpcm_play = |
458 | cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; | 480 | cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; |
459 | struct loopback_pcm *dpcm_capt = | 481 | struct loopback_pcm *dpcm_capt = |
460 | cable->streams[SNDRV_PCM_STREAM_CAPTURE]; | 482 | cable->streams[SNDRV_PCM_STREAM_CAPTURE]; |
461 | unsigned long delta_play = 0, delta_capt = 0; | 483 | unsigned long delta_play = 0, delta_capt = 0; |
484 | unsigned int running; | ||
462 | 485 | ||
463 | spin_lock(&cable->lock); | 486 | spin_lock(&cable->lock); |
464 | if (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { | 487 | running = cable->running ^ cable->pause; |
488 | if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { | ||
465 | delta_play = jiffies - dpcm_play->last_jiffies; | 489 | delta_play = jiffies - dpcm_play->last_jiffies; |
466 | dpcm_play->last_jiffies += delta_play; | 490 | dpcm_play->last_jiffies += delta_play; |
467 | } | 491 | } |
468 | 492 | ||
469 | if (cable->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { | 493 | if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { |
470 | delta_capt = jiffies - dpcm_capt->last_jiffies; | 494 | delta_capt = jiffies - dpcm_capt->last_jiffies; |
471 | dpcm_capt->last_jiffies += delta_capt; | 495 | dpcm_capt->last_jiffies += delta_capt; |
472 | } | 496 | } |
473 | 497 | ||
474 | if (delta_play == 0 && delta_capt == 0) { | 498 | if (delta_play == 0 && delta_capt == 0) { |
475 | spin_unlock(&cable->lock); | 499 | spin_unlock(&cable->lock); |
476 | return; | 500 | return running; |
477 | } | 501 | } |
478 | 502 | ||
479 | if (delta_play > delta_capt) { | 503 | if (delta_play > delta_capt) { |
@@ -488,27 +512,27 @@ static void loopback_pos_update(struct loopback_cable *cable) | |||
488 | 512 | ||
489 | if (delta_play == 0 && delta_capt == 0) { | 513 | if (delta_play == 0 && delta_capt == 0) { |
490 | spin_unlock(&cable->lock); | 514 | spin_unlock(&cable->lock); |
491 | return; | 515 | return running; |
492 | } | 516 | } |
493 | /* note delta_capt == delta_play at this moment */ | 517 | /* note delta_capt == delta_play at this moment */ |
494 | loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); | 518 | loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); |
495 | loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); | 519 | loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); |
496 | spin_unlock(&cable->lock); | 520 | spin_unlock(&cable->lock); |
521 | return running; | ||
497 | } | 522 | } |
498 | 523 | ||
499 | static void loopback_timer_function(unsigned long data) | 524 | static void loopback_timer_function(unsigned long data) |
500 | { | 525 | { |
501 | struct loopback_pcm *dpcm = (struct loopback_pcm *)data; | 526 | struct loopback_pcm *dpcm = (struct loopback_pcm *)data; |
502 | int stream; | 527 | unsigned int running; |
503 | 528 | ||
504 | loopback_pos_update(dpcm->cable); | 529 | running = loopback_pos_update(dpcm->cable); |
505 | stream = dpcm->substream->stream; | 530 | if (running & (1 << dpcm->substream->stream)) { |
506 | if (dpcm->cable->running & (1 << stream)) | ||
507 | loopback_timer_start(dpcm); | 531 | loopback_timer_start(dpcm); |
508 | if (dpcm->period_update_pending) { | 532 | if (dpcm->period_update_pending) { |
509 | dpcm->period_update_pending = 0; | 533 | dpcm->period_update_pending = 0; |
510 | if (dpcm->cable->running & (1 << stream)) | ||
511 | snd_pcm_period_elapsed(dpcm->substream); | 534 | snd_pcm_period_elapsed(dpcm->substream); |
535 | } | ||
512 | } | 536 | } |
513 | } | 537 | } |
514 | 538 | ||
@@ -524,7 +548,7 @@ static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) | |||
524 | static struct snd_pcm_hardware loopback_pcm_hardware = | 548 | static struct snd_pcm_hardware loopback_pcm_hardware = |
525 | { | 549 | { |
526 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | | 550 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | |
527 | SNDRV_PCM_INFO_MMAP_VALID), | 551 | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), |
528 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | | 552 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | |
529 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | | 553 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | |
530 | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), | 554 | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), |
@@ -1011,6 +1035,87 @@ static int __devinit loopback_mixer_new(struct loopback *loopback, int notify) | |||
1011 | return 0; | 1035 | return 0; |
1012 | } | 1036 | } |
1013 | 1037 | ||
1038 | #ifdef CONFIG_PROC_FS | ||
1039 | |||
1040 | static void print_dpcm_info(struct snd_info_buffer *buffer, | ||
1041 | struct loopback_pcm *dpcm, | ||
1042 | const char *id) | ||
1043 | { | ||
1044 | snd_iprintf(buffer, " %s\n", id); | ||
1045 | if (dpcm == NULL) { | ||
1046 | snd_iprintf(buffer, " inactive\n"); | ||
1047 | return; | ||
1048 | } | ||
1049 | snd_iprintf(buffer, " buffer_size:\t%u\n", dpcm->pcm_buffer_size); | ||
1050 | snd_iprintf(buffer, " buffer_pos:\t\t%u\n", dpcm->buf_pos); | ||
1051 | snd_iprintf(buffer, " silent_size:\t%u\n", dpcm->silent_size); | ||
1052 | snd_iprintf(buffer, " period_size:\t%u\n", dpcm->pcm_period_size); | ||
1053 | snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps); | ||
1054 | snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign); | ||
1055 | snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift); | ||
1056 | snd_iprintf(buffer, " update_pending:\t%u\n", | ||
1057 | dpcm->period_update_pending); | ||
1058 | snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); | ||
1059 | snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); | ||
1060 | snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", | ||
1061 | dpcm->last_jiffies, jiffies); | ||
1062 | snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); | ||
1063 | } | ||
1064 | |||
1065 | static void print_substream_info(struct snd_info_buffer *buffer, | ||
1066 | struct loopback *loopback, | ||
1067 | int sub, | ||
1068 | int num) | ||
1069 | { | ||
1070 | struct loopback_cable *cable = loopback->cables[sub][num]; | ||
1071 | |||
1072 | snd_iprintf(buffer, "Cable %i substream %i:\n", num, sub); | ||
1073 | if (cable == NULL) { | ||
1074 | snd_iprintf(buffer, " inactive\n"); | ||
1075 | return; | ||
1076 | } | ||
1077 | snd_iprintf(buffer, " valid: %u\n", cable->valid); | ||
1078 | snd_iprintf(buffer, " running: %u\n", cable->running); | ||
1079 | snd_iprintf(buffer, " pause: %u\n", cable->pause); | ||
1080 | print_dpcm_info(buffer, cable->streams[0], "Playback"); | ||
1081 | print_dpcm_info(buffer, cable->streams[1], "Capture"); | ||
1082 | } | ||
1083 | |||
1084 | static void print_cable_info(struct snd_info_entry *entry, | ||
1085 | struct snd_info_buffer *buffer) | ||
1086 | { | ||
1087 | struct loopback *loopback = entry->private_data; | ||
1088 | int sub, num; | ||
1089 | |||
1090 | mutex_lock(&loopback->cable_lock); | ||
1091 | num = entry->name[strlen(entry->name)-1]; | ||
1092 | num = num == '0' ? 0 : 1; | ||
1093 | for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++) | ||
1094 | print_substream_info(buffer, loopback, sub, num); | ||
1095 | mutex_unlock(&loopback->cable_lock); | ||
1096 | } | ||
1097 | |||
1098 | static int __devinit loopback_proc_new(struct loopback *loopback, int cidx) | ||
1099 | { | ||
1100 | char name[32]; | ||
1101 | struct snd_info_entry *entry; | ||
1102 | int err; | ||
1103 | |||
1104 | snprintf(name, sizeof(name), "cable#%d", cidx); | ||
1105 | err = snd_card_proc_new(loopback->card, name, &entry); | ||
1106 | if (err < 0) | ||
1107 | return err; | ||
1108 | |||
1109 | snd_info_set_text_ops(entry, loopback, print_cable_info); | ||
1110 | return 0; | ||
1111 | } | ||
1112 | |||
1113 | #else /* !CONFIG_PROC_FS */ | ||
1114 | |||
1115 | #define loopback_proc_new(loopback, cidx) do { } while (0) | ||
1116 | |||
1117 | #endif | ||
1118 | |||
1014 | static int __devinit loopback_probe(struct platform_device *devptr) | 1119 | static int __devinit loopback_probe(struct platform_device *devptr) |
1015 | { | 1120 | { |
1016 | struct snd_card *card; | 1121 | struct snd_card *card; |
@@ -1041,6 +1146,8 @@ static int __devinit loopback_probe(struct platform_device *devptr) | |||
1041 | err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0); | 1146 | err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0); |
1042 | if (err < 0) | 1147 | if (err < 0) |
1043 | goto __nodev; | 1148 | goto __nodev; |
1149 | loopback_proc_new(loopback, 0); | ||
1150 | loopback_proc_new(loopback, 1); | ||
1044 | strcpy(card->driver, "Loopback"); | 1151 | strcpy(card->driver, "Loopback"); |
1045 | strcpy(card->shortname, "Loopback"); | 1152 | strcpy(card->shortname, "Loopback"); |
1046 | sprintf(card->longname, "Loopback %i", dev + 1); | 1153 | sprintf(card->longname, "Loopback %i", dev + 1); |