diff options
Diffstat (limited to 'sound/drivers/aloop.c')
| -rw-r--r-- | sound/drivers/aloop.c | 144 |
1 files changed, 82 insertions, 62 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index ad079b63b8b..2a16c86a60b 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -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), @@ -758,8 +777,8 @@ static struct snd_pcm_ops loopback_capture_ops = { .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; @@ -933,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", @@ -982,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; @@ -1095,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; @@ -1116,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; @@ -1161,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); @@ -1182,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, }, }; |
