diff options
Diffstat (limited to 'sound/drivers/aloop.c')
| -rw-r--r-- | sound/drivers/aloop.c | 161 |
1 files changed, 91 insertions, 70 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index a0da7755fce..2a16c86a60b 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -34,7 +34,7 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/wait.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <sound/core.h> #include <sound/control.h> @@ -51,7 +51,7 @@ MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; +static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; static int pcm_notify[SNDRV_CARDS]; @@ -117,6 +117,7 @@ struct loopback_pcm { /* timer stuff */ unsigned int irq_pos; /* fractional IRQ position */ unsigned int period_size_frac; + unsigned int last_drift; unsigned long last_jiffies; struct timer_list timer; }; @@ -164,6 +165,7 @@ static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm) return get_setup(dpcm)->rate_shift; } +/* call in cable->lock */ static void loopback_timer_start(struct loopback_pcm *dpcm) { unsigned long tick; @@ -183,6 +185,7 @@ static void loopback_timer_start(struct loopback_pcm *dpcm) add_timer(&dpcm->timer); } +/* call in cable->lock */ static inline void loopback_timer_stop(struct loopback_pcm *dpcm) { del_timer(&dpcm->timer); @@ -264,11 +267,12 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) return err; dpcm->last_jiffies = jiffies; dpcm->pcm_rate_shift = 0; + dpcm->last_drift = 0; spin_lock(&cable->lock); cable->running |= stream; cable->pause &= ~stream; - spin_unlock(&cable->lock); loopback_timer_start(dpcm); + spin_unlock(&cable->lock); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) loopback_active_notify(dpcm); break; @@ -276,23 +280,25 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock(&cable->lock); cable->running &= ~stream; cable->pause &= ~stream; - spin_unlock(&cable->lock); loopback_timer_stop(dpcm); + spin_unlock(&cable->lock); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) loopback_active_notify(dpcm); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: spin_lock(&cable->lock); cable->pause |= stream; - spin_unlock(&cable->lock); loopback_timer_stop(dpcm); + spin_unlock(&cable->lock); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: spin_lock(&cable->lock); dpcm->last_jiffies = jiffies; cable->pause &= ~stream; - spin_unlock(&cable->lock); loopback_timer_start(dpcm); + spin_unlock(&cable->lock); break; default: return -EINVAL; @@ -319,7 +325,7 @@ static void params_change(struct snd_pcm_substream *substream) struct loopback_pcm *dpcm = runtime->private_data; struct loopback_cable *cable = dpcm->cable; - cable->hw.formats = (1ULL << runtime->format); + cable->hw.formats = pcm_format_to_bits(runtime->format); cable->hw.rate_min = runtime->rate; cable->hw.rate_max = runtime->rate; cable->hw.channels_min = runtime->channels; @@ -444,36 +450,33 @@ static void copy_play_buf(struct loopback_pcm *play, } } -#define BYTEPOS_UPDATE_POSONLY 0 -#define BYTEPOS_UPDATE_CLEAR 1 -#define BYTEPOS_UPDATE_COPY 2 - -static void loopback_bytepos_update(struct loopback_pcm *dpcm, - unsigned int delta, - unsigned int cmd) +static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm, + unsigned int jiffies_delta) { - unsigned int count; unsigned long last_pos; + unsigned int delta; last_pos = byte_pos(dpcm, dpcm->irq_pos); - dpcm->irq_pos += delta * dpcm->pcm_bps; - count = byte_pos(dpcm, dpcm->irq_pos) - last_pos; - if (!count) - return; - if (cmd == BYTEPOS_UPDATE_CLEAR) - clear_capture_buf(dpcm, count); - else if (cmd == BYTEPOS_UPDATE_COPY) - copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK], - dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE], - count); - dpcm->buf_pos += count; - dpcm->buf_pos %= dpcm->pcm_buffer_size; + dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps; + delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos; + if (delta >= dpcm->last_drift) + delta -= dpcm->last_drift; + dpcm->last_drift = 0; if (dpcm->irq_pos >= dpcm->period_size_frac) { dpcm->irq_pos %= dpcm->period_size_frac; dpcm->period_update_pending = 1; } + return delta; } +static inline void bytepos_finish(struct loopback_pcm *dpcm, + unsigned int delta) +{ + dpcm->buf_pos += delta; + dpcm->buf_pos %= dpcm->pcm_buffer_size; +} + +/* call in cable->lock */ static unsigned int loopback_pos_update(struct loopback_cable *cable) { struct loopback_pcm *dpcm_play = @@ -481,10 +484,8 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) struct loopback_pcm *dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE]; unsigned long delta_play = 0, delta_capt = 0; - unsigned int running; - unsigned long flags; + unsigned int running, count1, count2; - spin_lock_irqsave(&cable->lock, flags); running = cable->running ^ cable->pause; if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { delta_play = jiffies - dpcm_play->last_jiffies; @@ -500,12 +501,13 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) goto unlock; if (delta_play > delta_capt) { - loopback_bytepos_update(dpcm_play, delta_play - delta_capt, - BYTEPOS_UPDATE_POSONLY); + count1 = bytepos_delta(dpcm_play, delta_play - delta_capt); + bytepos_finish(dpcm_play, count1); delta_play = delta_capt; } else if (delta_play < delta_capt) { - loopback_bytepos_update(dpcm_capt, delta_capt - delta_play, - BYTEPOS_UPDATE_CLEAR); + count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play); + clear_capture_buf(dpcm_capt, count1); + bytepos_finish(dpcm_capt, count1); delta_capt = delta_play; } @@ -513,41 +515,58 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) goto unlock; /* note delta_capt == delta_play at this moment */ - loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); - loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); + count1 = bytepos_delta(dpcm_play, delta_play); + count2 = bytepos_delta(dpcm_capt, delta_capt); + if (count1 < count2) { + dpcm_capt->last_drift = count2 - count1; + count1 = count2; + } else if (count1 > count2) { + dpcm_play->last_drift = count1 - count2; + } + copy_play_buf(dpcm_play, dpcm_capt, count1); + bytepos_finish(dpcm_play, count1); + bytepos_finish(dpcm_capt, count1); unlock: - spin_unlock_irqrestore(&cable->lock, flags); return running; } static void loopback_timer_function(unsigned long data) { struct loopback_pcm *dpcm = (struct loopback_pcm *)data; - unsigned int running; + unsigned long flags; - running = loopback_pos_update(dpcm->cable); - if (running & (1 << dpcm->substream->stream)) { + spin_lock_irqsave(&dpcm->cable->lock, flags); + if (loopback_pos_update(dpcm->cable) & (1 << dpcm->substream->stream)) { loopback_timer_start(dpcm); if (dpcm->period_update_pending) { dpcm->period_update_pending = 0; + spin_unlock_irqrestore(&dpcm->cable->lock, flags); + /* need to unlock before calling below */ snd_pcm_period_elapsed(dpcm->substream); + return; } } + spin_unlock_irqrestore(&dpcm->cable->lock, flags); } static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; + snd_pcm_uframes_t pos; + spin_lock(&dpcm->cable->lock); loopback_pos_update(dpcm->cable); - return bytes_to_frames(runtime, dpcm->buf_pos); + pos = dpcm->buf_pos; + spin_unlock(&dpcm->cable->lock); + return bytes_to_frames(runtime, pos); } static struct snd_pcm_hardware loopback_pcm_hardware = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), @@ -575,7 +594,8 @@ static void loopback_runtime_free(struct snd_pcm_runtime *runtime) static int loopback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(params)); } static int loopback_hw_free(struct snd_pcm_substream *substream) @@ -587,7 +607,7 @@ static int loopback_hw_free(struct snd_pcm_substream *substream) mutex_lock(&dpcm->loopback->cable_lock); cable->valid &= ~(1 << substream->stream); mutex_unlock(&dpcm->loopback->cable_lock); - return snd_pcm_lib_free_pages(substream); + return snd_pcm_lib_free_vmalloc_buffer(substream); } static unsigned int get_cable_index(struct snd_pcm_substream *substream) @@ -740,6 +760,8 @@ static struct snd_pcm_ops loopback_playback_ops = { .prepare = loopback_prepare, .trigger = loopback_trigger, .pointer = loopback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, }; static struct snd_pcm_ops loopback_capture_ops = { @@ -751,10 +773,12 @@ static struct snd_pcm_ops loopback_capture_ops = { .prepare = loopback_prepare, .trigger = loopback_trigger, .pointer = loopback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, }; -static int __devinit loopback_pcm_new(struct loopback *loopback, - int device, int substreams) +static int loopback_pcm_new(struct loopback *loopback, + int device, int substreams) { struct snd_pcm *pcm; int err; @@ -771,10 +795,6 @@ static int __devinit loopback_pcm_new(struct loopback *loopback, strcpy(pcm->name, "Loopback PCM"); loopback->pcm[device] = pcm; - - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - 0, 2 * 1024 * 1024); return 0; } @@ -932,7 +952,7 @@ static int loopback_channels_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new loopback_controls[] __devinitdata = { +static struct snd_kcontrol_new loopback_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "PCM Rate Shift 100000", @@ -981,7 +1001,7 @@ static struct snd_kcontrol_new loopback_controls[] __devinitdata = { } }; -static int __devinit loopback_mixer_new(struct loopback *loopback, int notify) +static int loopback_mixer_new(struct loopback *loopback, int notify) { struct snd_card *card = loopback->card; struct snd_pcm *pcm; @@ -1094,7 +1114,7 @@ static void print_cable_info(struct snd_info_entry *entry, mutex_unlock(&loopback->cable_lock); } -static int __devinit loopback_proc_new(struct loopback *loopback, int cidx) +static int loopback_proc_new(struct loopback *loopback, int cidx) { char name[32]; struct snd_info_entry *entry; @@ -1115,15 +1135,15 @@ static int __devinit loopback_proc_new(struct loopback *loopback, int cidx) #endif -static int __devinit loopback_probe(struct platform_device *devptr) +static int loopback_probe(struct platform_device *devptr) { struct snd_card *card; struct loopback *loopback; int dev = devptr->id; int err; - err = snd_card_create(index[dev], id[dev], THIS_MODULE, - sizeof(struct loopback), &card); + err = snd_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, + sizeof(struct loopback), &card); if (err < 0) return err; loopback = card->private_data; @@ -1160,18 +1180,16 @@ static int __devinit loopback_probe(struct platform_device *devptr) return err; } -static int __devexit loopback_remove(struct platform_device *devptr) +static int loopback_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); return 0; } -#ifdef CONFIG_PM -static int loopback_suspend(struct platform_device *pdev, - pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int loopback_suspend(struct device *pdev) { - struct snd_card *card = platform_get_drvdata(pdev); + struct snd_card *card = dev_get_drvdata(pdev); struct loopback *loopback = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -1181,26 +1199,29 @@ static int loopback_suspend(struct platform_device *pdev, return 0; } -static int loopback_resume(struct platform_device *pdev) +static int loopback_resume(struct device *pdev) { - struct snd_card *card = platform_get_drvdata(pdev); + struct snd_card *card = dev_get_drvdata(pdev); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume); +#define LOOPBACK_PM_OPS &loopback_pm +#else +#define LOOPBACK_PM_OPS NULL #endif #define SND_LOOPBACK_DRIVER "snd_aloop" static struct platform_driver loopback_driver = { .probe = loopback_probe, - .remove = __devexit_p(loopback_remove), -#ifdef CONFIG_PM - .suspend = loopback_suspend, - .resume = loopback_resume, -#endif + .remove = loopback_remove, .driver = { - .name = SND_LOOPBACK_DRIVER + .name = SND_LOOPBACK_DRIVER, + .owner = THIS_MODULE, + .pm = LOOPBACK_PM_OPS, }, }; |
