diff -drupN a/sound/drivers/aloop.c b/sound/drivers/aloop.c --- a/sound/drivers/aloop.c 2018-08-06 17:23:04.000000000 +0300 +++ b/sound/drivers/aloop.c 2022-06-12 05:28:14.000000000 +0300 @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -48,6 +47,39 @@ MODULE_DESCRIPTION("A loopback soundcard MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}"); +#ifdef CONFIG_SND_ALOOP_USE_HRTIMER +#define SND_ALOOP_USE_HRTIMER +#endif + +#ifdef CONFIG_SND_SUN8IW8_TRIGGER_SYNC_WITH_ALOOP +#include "../soc/sunxi/sun8iw8/sun8iw8_sndcodec.h" +#endif + +#ifdef SND_ALOOP_USE_HRTIMER +#include +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC + +#ifndef U32_MAX +#define U32_MAX ((u32)~0U) +#endif /* U32_MAX */ + +#if defined CONFIG_SND_ALOOP_RUN_SYNC_WITH_SUN8IW8 +#include "../soc/sunxi/sun8iw8/sun8iw8_sndcodec.h" +static inline u32 loopback_get_playback_sample_cnt(void) +{ + return sun8iw8_codec_get_dac_cnt(); +} +static inline u32 loopback_get_capture_sample_cnt(void) +{ + return sun8iw8_codec_get_adc_cnt(); +} +#else /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_SUN8IW8 */ +static inline u32 loopback_get_playback_sample_cnt(void) { return 0; } +static inline u32 loopback_get_capture_sample_cnt(void) { return 0; } +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_SUN8IW8 */ +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ +#endif /* SND_ALOOP_USE_HRTIMER */ + #define MAX_PCM_SUBSTREAMS 8 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -81,6 +113,34 @@ struct loopback_cable { unsigned int pause; }; +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC +#define SYNC_PCM_STREAM_CAPTURE 0 +#define SYNC_PCM_STREAM_PLAYBACK 1 +static const char *loopback_sync_pcm_stream_text[] = { + "Capture", + "Playback", +}; + +struct loopback_kcontrol_enum { + unsigned int items; + const char * const *texts; +}; + +static const struct loopback_kcontrol_enum loopback_sync_pcm_stream_enum = { + .items = ARRAY_SIZE(loopback_sync_pcm_stream_text), + .texts = loopback_sync_pcm_stream_text, +}; + +#define SYNC_PCM_DEFAULT_RATE 48000 +#define SYNC_PCM_DEFAULT_CHANNELS 2 +#define SYNC_PCM_DEFAULT_STREAM SYNC_PCM_STREAM_CAPTURE +struct loopback_sync_pcm_setup { + unsigned int rate; + unsigned int channels; + unsigned int stream; +}; +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + struct loopback_setup { unsigned int notify: 1; unsigned int rate_shift; @@ -91,6 +151,9 @@ struct loopback_setup { struct snd_ctl_elem_id format_id; struct snd_ctl_elem_id rate_id; struct snd_ctl_elem_id channels_id; +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC + struct loopback_sync_pcm_setup sync_pcm; +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ }; struct loopback { @@ -116,11 +179,21 @@ struct loopback_pcm { /* flags */ unsigned int period_update_pending :1; /* timer stuff */ +#ifdef SND_ALOOP_USE_HRTIMER + unsigned int irq_pos; /* IRQ position (not fractional) */ + struct hrtimer timer; +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC + u32 last_sample_cnt; +#else + struct timespec last_time; +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ +#else /* SND_ALOOP_USE_HRTIMER */ unsigned int irq_pos; /* fractional IRQ position */ unsigned int period_size_frac; unsigned int last_drift; unsigned long last_jiffies; struct timer_list timer; +#endif /* SND_ALOOP_USE_HRTIMER */ }; static struct platform_device *devices[SNDRV_CARDS]; @@ -150,7 +223,7 @@ static inline unsigned int frac_pos(stru static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm) { int device = dpcm->substream->pstr->pcm->device; - + if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) device ^= 1; return &dpcm->loopback->setup[dpcm->substream->number][device]; @@ -166,6 +239,89 @@ static inline unsigned int get_rate_shif return get_setup(dpcm)->rate_shift; } +#ifdef SND_ALOOP_USE_HRTIMER +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC +static u32 loopback_get_sample_cnt_single(struct loopback_pcm *dpcm) +{ + struct loopback_setup *setup = get_setup(dpcm); + + switch (setup->sync_pcm.stream) { + case SYNC_PCM_STREAM_CAPTURE: + return loopback_get_capture_sample_cnt(); + case SYNC_PCM_STREAM_PLAYBACK: + return loopback_get_playback_sample_cnt(); + default: + return 0; + } +} + +static void loopback_get_sample_cnt_double(struct loopback_pcm *dpcm_play, + struct loopback_pcm *dpcm_capt, + u32 *cnt_play, u32 *cnt_capt) +{ + struct loopback_setup *setup_play = get_setup(dpcm_play); + struct loopback_setup *setup_capt = get_setup(dpcm_capt); + u32 cnt; + + if (setup_play->sync_pcm.stream == setup_capt->sync_pcm.stream) { + switch (setup_play->sync_pcm.stream) { + case SYNC_PCM_STREAM_CAPTURE: + cnt = loopback_get_capture_sample_cnt(); + break; + case SYNC_PCM_STREAM_PLAYBACK: + cnt = loopback_get_playback_sample_cnt(); + break; + default: + cnt = 0; + break; + } + *cnt_play = cnt; + *cnt_capt = cnt; + } else { + *cnt_play = loopback_get_sample_cnt_single(dpcm_play); + *cnt_capt = loopback_get_sample_cnt_single(dpcm_capt); + } +} +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + +static void get_monotonic_in_ms_res(struct timespec *ts) +{ + getrawmonotonic(ts); + ts->tv_nsec = ts->tv_nsec / NSEC_PER_MSEC * NSEC_PER_MSEC; +} + +static void loopback_timer_start(struct loopback_pcm *dpcm) +{ + unsigned int rate_shift = get_rate_shift(dpcm); + u64 expires_ns; + ktime_t expires; + +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC +#else + get_monotonic_in_ms_res(&dpcm->last_time); +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + + if (rate_shift != dpcm->pcm_rate_shift) + dpcm->pcm_rate_shift = rate_shift; + + if (dpcm->irq_pos >= dpcm->pcm_period_size) { + dpcm->irq_pos %= dpcm->pcm_period_size; + dpcm->period_update_pending = 1; + } + + expires_ns = div_u64((u64)(dpcm->pcm_period_size) * (u64)NSEC_PER_SEC + + (u64)(dpcm->pcm_bps - 1), + dpcm->pcm_bps); + expires = ns_to_ktime(expires_ns); + hrtimer_start(&dpcm->timer, expires, HRTIMER_MODE_REL); +} + +static void loopback_timer_stop(struct loopback_pcm *dpcm) +{ + if (!hrtimer_callback_running(&dpcm->timer)) + hrtimer_cancel(&dpcm->timer); +} +#else /* SND_ALOOP_USE_HRTIMER */ /* call in cable->lock */ static void loopback_timer_start(struct loopback_pcm *dpcm) { @@ -191,11 +347,7 @@ static inline void loopback_timer_stop(s del_timer(&dpcm->timer); dpcm->timer.expires = 0; } - -static inline void loopback_timer_stop_sync(struct loopback_pcm *dpcm) -{ - del_timer_sync(&dpcm->timer); -} +#endif /* SND_ALOOP_USE_HRTIMER */ #define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) #define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE) @@ -258,6 +410,103 @@ static void loopback_active_notify(struc &get_setup(dpcm)->active_id); } +static int loopback_trigger_additional_ops( + struct snd_pcm_substream *substream, int cmd) +{ +#ifdef CONFIG_SND_SUN8IW8_TRIGGER_SYNC_WITH_ALOOP + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + sun8iw8_codec_dac_drq_enable(1); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + sun8iw8_codec_dac_drq_enable(0); + return 0; + default: + return -EINVAL; + } + } else { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + sun8iw8_codec_adc_drq_enable(1); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + sun8iw8_codec_adc_drq_enable(0); + return 0; + default: + return -EINVAL; + } + } +#else /* CONFIG_SND_SUN8IW8_TRIGGER_SYNC_WITH_ALOOP */ + return 0; +#endif /* CONFIG_SND_SUN8IW8_TRIGGER_SYNC_WITH_ALOOP */ +} + +#ifdef SND_ALOOP_USE_HRTIMER +static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + struct loopback_cable *cable = dpcm->cable; + int err, stream = 1 << substream->stream; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = loopback_check_format(cable, substream->stream); + if (err < 0) + return err; +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC + dpcm->last_sample_cnt = loopback_get_sample_cnt_single(dpcm); +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + dpcm->pcm_rate_shift = 0; + spin_lock(&cable->lock); + cable->running |= stream; + cable->pause &= ~stream; + spin_unlock(&cable->lock); + loopback_timer_start(dpcm); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + loopback_active_notify(dpcm); + break; + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&cable->lock); + cable->running &= ~stream; + cable->pause &= ~stream; + spin_unlock(&cable->lock); + loopback_timer_stop(dpcm); + 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); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC + dpcm->last_sample_cnt = loopback_get_sample_cnt_single(dpcm); +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + spin_lock(&cable->lock); + cable->pause &= ~stream; + spin_unlock(&cable->lock); + loopback_timer_start(dpcm); + break; + default: + return -EINVAL; + } + return loopback_trigger_additional_ops(substream, cmd); +} +#else /* SND_ALOOP_USE_HRTIMER */ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -273,7 +522,7 @@ static int loopback_trigger(struct snd_p dpcm->last_jiffies = jiffies; dpcm->pcm_rate_shift = 0; dpcm->last_drift = 0; - spin_lock(&cable->lock); + spin_lock(&cable->lock); cable->running |= stream; cable->pause &= ~stream; loopback_timer_start(dpcm); @@ -282,7 +531,7 @@ static int loopback_trigger(struct snd_p loopback_active_notify(dpcm); break; case SNDRV_PCM_TRIGGER_STOP: - spin_lock(&cable->lock); + spin_lock(&cable->lock); cable->running &= ~stream; cable->pause &= ~stream; loopback_timer_stop(dpcm); @@ -292,12 +541,10 @@ static int loopback_trigger(struct snd_p break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: - spin_lock(&cable->lock); + spin_lock(&cable->lock); cable->pause |= stream; 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_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: @@ -306,13 +553,25 @@ static int loopback_trigger(struct snd_p cable->pause &= ~stream; loopback_timer_start(dpcm); spin_unlock(&cable->lock); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - loopback_active_notify(dpcm); break; default: return -EINVAL; } - return 0; + return loopback_trigger_additional_ops(substream, cmd); +} +#endif /* SND_ALOOP_USE_HRTIMER */ + +static void params_change_substream(struct loopback_pcm *dpcm, + struct snd_pcm_runtime *runtime) +{ + struct snd_pcm_runtime *dst_runtime; + + if (dpcm == NULL || dpcm->substream == NULL) + return; + dst_runtime = dpcm->substream->runtime; + if (dst_runtime == NULL) + return; + dst_runtime->hw = dpcm->cable->hw; } static void params_change(struct snd_pcm_substream *substream) @@ -326,6 +585,10 @@ static void params_change(struct snd_pcm cable->hw.rate_max = runtime->rate; cable->hw.channels_min = runtime->channels; cable->hw.channels_max = runtime->channels; + params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK], + runtime); + params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE], + runtime); } static int loopback_prepare(struct snd_pcm_substream *substream) @@ -335,8 +598,6 @@ static int loopback_prepare(struct snd_p struct loopback_cable *cable = dpcm->cable; int bps, salign; - loopback_timer_stop_sync(dpcm); - salign = (snd_pcm_format_width(runtime->format) * runtime->channels) / 8; bps = salign * runtime->rate; @@ -360,8 +621,8 @@ static int loopback_prepare(struct snd_p mutex_lock(&dpcm->loopback->cable_lock); if (!(cable->valid & ~(1 << substream->stream)) || - (get_setup(dpcm)->notify && - substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) + (get_setup(dpcm)->notify && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) params_change(substream); cable->valid |= 1 << substream->stream; mutex_unlock(&dpcm->loopback->cable_lock); @@ -385,8 +646,8 @@ static void clear_capture_buf(struct loo if (dst_off + size > dpcm->pcm_buffer_size) size = dpcm->pcm_buffer_size - dst_off; snd_pcm_format_set_silence(runtime->format, dst + dst_off, - bytes_to_frames(runtime, size) * - runtime->channels); + bytes_to_frames(runtime, size) * + runtime->channels); dpcm->silent_size += size; bytes -= size; if (!bytes) @@ -409,8 +670,8 @@ static void copy_play_buf(struct loopbac /* check if playback is draining, trim the capture copy size * when our pointer is at the end of playback ring buffer */ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING && - snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { - snd_pcm_uframes_t appl_ptr, appl_ptr1, diff; + snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { + snd_pcm_uframes_t appl_ptr, appl_ptr1, diff; appl_ptr = appl_ptr1 = runtime->control->appl_ptr; appl_ptr1 -= appl_ptr1 % runtime->buffer_size; appl_ptr1 += play->buf_pos / play->pcm_salign; @@ -444,6 +705,190 @@ static void copy_play_buf(struct loopbac } } +#ifdef SND_ALOOP_USE_HRTIMER +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC +static unsigned int sample_cnt_to_frames(struct loopback_pcm *dpcm, + u32 current_cnt, u32 last_cnt) +{ + struct snd_pcm_runtime *runtime = dpcm->substream->runtime; + struct loopback_setup *setup = get_setup(dpcm); + u32 delta_cnt; + + if (current_cnt < last_cnt) + delta_cnt = current_cnt + (U32_MAX - last_cnt); + else + delta_cnt = current_cnt - last_cnt; + + /* FIXME? + * Currently rounding down always, or standard rounding may be better? + */ + return delta_cnt * runtime->rate + / (setup->sync_pcm.channels * setup->sync_pcm.rate); +} +#else /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ +static unsigned int time_to_frames(struct loopback_pcm *dpcm, struct timespec t) +{ + s64 ms = (s64)(t.tv_sec) * MSEC_PER_SEC + + (s64)(t.tv_nsec / NSEC_PER_MSEC); + return (unsigned int)div_s64( + ms * (s64)(dpcm->pcm_bps / dpcm->pcm_salign), + MSEC_PER_SEC); +} +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + +#define BYTEPOS_UPDATE_POSONLY 0 +#define BYTEPOS_UPDATE_CLEAR 1 +#define BYTEPOS_UPDATE_COPY 2 + +static void loopback_bytepos_update(struct loopback_pcm *dpcm, + s64 delta_frames, unsigned int cmd) +{ + unsigned int count = delta_frames * dpcm->pcm_salign; + + 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 += count; + if (dpcm->irq_pos >= dpcm->pcm_period_size) { + dpcm->irq_pos %= dpcm->pcm_period_size; + dpcm->period_update_pending = 1; + } +} + +static unsigned int loopback_pos_update(struct loopback_cable *cable) +{ + struct loopback_pcm *dpcm_play = + cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; + struct loopback_pcm *dpcm_capt = + cable->streams[SNDRV_PCM_STREAM_CAPTURE]; + unsigned int running; + unsigned long flags; + unsigned int delta_play_frames = 0, delta_capt_frames = 0; +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC + u32 current_play_sample_cnt, current_capt_sample_cnt; + unsigned int running_play, running_capt; +#else + struct timespec current_time; + struct timespec delta_play_time, delta_capt_time; +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + + spin_lock_irqsave(&cable->lock, flags); + running = cable->running ^ cable->pause; + +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC + running_play = running & (1 << SNDRV_PCM_STREAM_PLAYBACK); + running_capt = running & (1 << SNDRV_PCM_STREAM_CAPTURE); + if (running_play && running_capt) + loopback_get_sample_cnt_double(dpcm_play, dpcm_capt, + ¤t_play_sample_cnt, + ¤t_capt_sample_cnt); + else if (running_play) + current_play_sample_cnt = + loopback_get_sample_cnt_single(dpcm_play); + else if (running_capt) + current_capt_sample_cnt = + loopback_get_sample_cnt_single(dpcm_capt); + + if (running_play) { + delta_play_frames = sample_cnt_to_frames(dpcm_play, + current_play_sample_cnt, dpcm_play->last_sample_cnt); + dpcm_play->last_sample_cnt = current_play_sample_cnt; + } + if (running_capt) { + delta_capt_frames = sample_cnt_to_frames(dpcm_capt, + current_capt_sample_cnt, dpcm_capt->last_sample_cnt); + dpcm_capt->last_sample_cnt = current_capt_sample_cnt; + } +#else /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + get_monotonic_in_ms_res(¤t_time); + if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { + delta_play_time = timespec_sub(current_time, + dpcm_play->last_time); + delta_play_frames = time_to_frames(dpcm_play, delta_play_time); + dpcm_play->last_time = current_time; + } + if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { + delta_capt_time = timespec_sub(current_time, + dpcm_capt->last_time); + delta_capt_frames = time_to_frames(dpcm_capt, delta_capt_time); + dpcm_capt->last_time = current_time; + } +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + + if (delta_play_frames == 0 && delta_capt_frames == 0) + goto unlock; + + if (delta_play_frames > delta_capt_frames) { + loopback_bytepos_update(dpcm_play, + delta_play_frames - delta_capt_frames, + BYTEPOS_UPDATE_POSONLY); + delta_play_frames = delta_capt_frames; + } else if (delta_play_frames < delta_capt_frames) { + loopback_bytepos_update(dpcm_capt, + delta_capt_frames - delta_play_frames, + BYTEPOS_UPDATE_CLEAR); + delta_capt_frames = delta_play_frames; + } + + if (delta_play_frames == 0 && delta_capt_frames == 0) + goto unlock; + + loopback_bytepos_update(dpcm_capt, delta_capt_frames, + BYTEPOS_UPDATE_COPY); + loopback_bytepos_update(dpcm_play, delta_play_frames, + BYTEPOS_UPDATE_POSONLY); + unlock: + spin_unlock_irqrestore(&cable->lock, flags); + return running; +} + +static enum hrtimer_restart loopback_timer_function(struct hrtimer *hrtimer) +{ + struct loopback_pcm *dpcm = + container_of(hrtimer, struct loopback_pcm, timer); + struct loopback_cable *cable = dpcm->cable; + unsigned int restart_timer; + unsigned int rate_shift = get_rate_shift(dpcm); + u64 expires_ns; + + restart_timer = + loopback_pos_update(cable) & (1 << dpcm->substream->stream); + + if (!restart_timer) + return HRTIMER_NORESTART; + + if (dpcm->period_update_pending) { + dpcm->period_update_pending = 0; + snd_pcm_period_elapsed(dpcm->substream); + } + + if (rate_shift != dpcm->pcm_rate_shift) + dpcm->pcm_rate_shift = rate_shift; + expires_ns = div_u64((u64)(dpcm->pcm_period_size) * (u64)NSEC_PER_SEC + + (u64)(dpcm->pcm_bps - 1), + dpcm->pcm_bps); + hrtimer_forward_now(&dpcm->timer, ns_to_ktime(expires_ns)); + return HRTIMER_RESTART; +} + +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; + + loopback_pos_update(dpcm->cable); + return bytes_to_frames(runtime, dpcm->buf_pos); +} + +#else /* SND_ALOOP_USE_HRTIMER */ + static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm, unsigned int jiffies_delta) { @@ -493,7 +938,7 @@ static unsigned int loopback_pos_update( if (delta_play == 0 && delta_capt == 0) goto unlock; - + if (delta_play > delta_capt) { count1 = bytepos_delta(dpcm_play, delta_play - delta_capt); bytepos_finish(dpcm_play, count1); @@ -555,9 +1000,9 @@ static snd_pcm_uframes_t loopback_pointe spin_unlock(&dpcm->cable->lock); return bytes_to_frames(runtime, pos); } +#endif /* SND_ALOOP_USE_HRTIMER */ -static struct snd_pcm_hardware loopback_pcm_hardware = -{ +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_RESUME), @@ -615,77 +1060,50 @@ static unsigned int get_cable_index(stru static int rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct loopback_pcm *dpcm = rule->private; - struct loopback_cable *cable = dpcm->cable; - struct snd_mask m; - snd_mask_none(&m); - mutex_lock(&dpcm->loopback->cable_lock); - m.bits[0] = (u_int32_t)cable->hw.formats; - m.bits[1] = (u_int32_t)(cable->hw.formats >> 32); - mutex_unlock(&dpcm->loopback->cable_lock); - return snd_mask_refine(hw_param_mask(params, rule->var), &m); + struct snd_pcm_hardware *hw = rule->private; + struct snd_mask *maskp = hw_param_mask(params, rule->var); + + maskp->bits[0] &= (u_int32_t)hw->formats; + maskp->bits[1] &= (u_int32_t)(hw->formats >> 32); + memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ + if (!maskp->bits[0] && !maskp->bits[1]) + return -EINVAL; + return 0; } static int rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct loopback_pcm *dpcm = rule->private; - struct loopback_cable *cable = dpcm->cable; + struct snd_pcm_hardware *hw = rule->private; struct snd_interval t; - mutex_lock(&dpcm->loopback->cable_lock); - t.min = cable->hw.rate_min; - t.max = cable->hw.rate_max; - mutex_unlock(&dpcm->loopback->cable_lock); - t.openmin = t.openmax = 0; - t.integer = 0; + t.min = hw->rate_min; + t.max = hw->rate_max; + t.openmin = t.openmax = 0; + t.integer = 0; return snd_interval_refine(hw_param_interval(params, rule->var), &t); } static int rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct loopback_pcm *dpcm = rule->private; - struct loopback_cable *cable = dpcm->cable; + struct snd_pcm_hardware *hw = rule->private; struct snd_interval t; - mutex_lock(&dpcm->loopback->cable_lock); - t.min = cable->hw.channels_min; - t.max = cable->hw.channels_max; - mutex_unlock(&dpcm->loopback->cable_lock); - t.openmin = t.openmax = 0; - t.integer = 0; + t.min = hw->channels_min; + t.max = hw->channels_max; + t.openmin = t.openmax = 0; + t.integer = 0; return snd_interval_refine(hw_param_interval(params, rule->var), &t); } -static void free_cable(struct snd_pcm_substream *substream) -{ - struct loopback *loopback = substream->private_data; - int dev = get_cable_index(substream); - struct loopback_cable *cable; - - cable = loopback->cables[substream->number][dev]; - if (!cable) - return; - if (cable->streams[!substream->stream]) { - /* other stream is still alive */ - spin_lock_irq(&cable->lock); - cable->streams[substream->stream] = NULL; - spin_unlock_irq(&cable->lock); - } else { - /* free the cable */ - loopback->cables[substream->number][dev] = NULL; - kfree(cable); - } -} - static int loopback_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct loopback *loopback = substream->private_data; struct loopback_pcm *dpcm; - struct loopback_cable *cable = NULL; + struct loopback_cable *cable; int err = 0; int dev = get_cable_index(substream); @@ -697,13 +1115,19 @@ static int loopback_open(struct snd_pcm_ } dpcm->loopback = loopback; dpcm->substream = substream; +#ifdef SND_ALOOP_USE_HRTIMER + hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + dpcm->timer.function = loopback_timer_function; +#else /* SND_ALOOP_USE_HRTIMER */ setup_timer(&dpcm->timer, loopback_timer_function, (unsigned long)dpcm); +#endif /* SND_ALOOP_USE_HRTIMER */ cable = loopback->cables[substream->number][dev]; if (!cable) { cable = kzalloc(sizeof(*cable), GFP_KERNEL); if (!cable) { + kfree(dpcm); err = -ENOMEM; goto unlock; } @@ -712,6 +1136,7 @@ static int loopback_open(struct snd_pcm_ loopback->cables[substream->number][dev] = cable; } dpcm->cable = cable; + cable->streams[substream->stream] = dpcm; snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -720,19 +1145,19 @@ static int loopback_open(struct snd_pcm_ /* are cached -> they do not reflect the actual state */ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - rule_format, dpcm, + rule_format, &runtime->hw, SNDRV_PCM_HW_PARAM_FORMAT, -1); if (err < 0) goto unlock; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - rule_rate, dpcm, + rule_rate, &runtime->hw, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto unlock; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - rule_channels, dpcm, + rule_channels, &runtime->hw, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto unlock; @@ -743,16 +1168,7 @@ static int loopback_open(struct snd_pcm_ runtime->hw = loopback_pcm_hardware; else runtime->hw = cable->hw; - - spin_lock_irq(&cable->lock); - cable->streams[substream->stream] = dpcm; - spin_unlock_irq(&cable->lock); - unlock: - if (err < 0) { - free_cable(substream); - kfree(dpcm); - } mutex_unlock(&loopback->cable_lock); return err; } @@ -761,10 +1177,20 @@ static int loopback_close(struct snd_pcm { struct loopback *loopback = substream->private_data; struct loopback_pcm *dpcm = substream->runtime->private_data; + struct loopback_cable *cable; + int dev = get_cable_index(substream); - loopback_timer_stop_sync(dpcm); + loopback_timer_stop(dpcm); mutex_lock(&loopback->cable_lock); - free_cable(substream); + cable = loopback->cables[substream->number][dev]; + if (cable->streams[!substream->stream]) { + /* other stream is still alive */ + cable->streams[substream->stream] = NULL; + } else { + /* free the cable */ + loopback->cables[substream->number][dev] = NULL; + kfree(cable); + } mutex_unlock(&loopback->cable_lock); return 0; } @@ -816,8 +1242,8 @@ static int loopback_pcm_new(struct loopb return 0; } -static int loopback_rate_shift_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int loopback_rate_shift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; @@ -825,18 +1251,16 @@ static int loopback_rate_shift_info(stru uinfo->value.integer.max = 120000; uinfo->value.integer.step = 1; return 0; -} +} static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct loopback *loopback = snd_kcontrol_chip(kcontrol); - - mutex_lock(&loopback->cable_lock); + ucontrol->value.integer.value[0] = loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].rate_shift; - mutex_unlock(&loopback->cable_lock); return 0; } @@ -851,7 +1275,7 @@ static int loopback_rate_shift_put(struc if (val < 80000) val = 80000; if (val > 120000) - val = 120000; + val = 120000; mutex_lock(&loopback->cable_lock); if (val != loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].rate_shift) { @@ -867,12 +1291,10 @@ static int loopback_notify_get(struct sn struct snd_ctl_elem_value *ucontrol) { struct loopback *loopback = snd_kcontrol_chip(kcontrol); - - mutex_lock(&loopback->cable_lock); + ucontrol->value.integer.value[0] = loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].notify; - mutex_unlock(&loopback->cable_lock); return 0; } @@ -884,14 +1306,12 @@ static int loopback_notify_put(struct sn int change = 0; val = ucontrol->value.integer.value[0] ? 1 : 0; - mutex_lock(&loopback->cable_lock); if (val != loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].notify) { loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].notify = val; change = 1; } - mutex_unlock(&loopback->cable_lock); return change; } @@ -899,24 +1319,19 @@ static int loopback_active_get(struct sn struct snd_ctl_elem_value *ucontrol) { struct loopback *loopback = snd_kcontrol_chip(kcontrol); - struct loopback_cable *cable; - + struct loopback_cable *cable = loopback->cables + [kcontrol->id.subdevice][kcontrol->id.device ^ 1]; unsigned int val = 0; - mutex_lock(&loopback->cable_lock); - cable = loopback->cables[kcontrol->id.subdevice][kcontrol->id.device ^ 1]; - if (cable != NULL) { - unsigned int running = cable->running ^ cable->pause; - - val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0; - } - mutex_unlock(&loopback->cable_lock); + if (cable != NULL) + val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? + 1 : 0; ucontrol->value.integer.value[0] = val; return 0; } -static int loopback_format_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int loopback_format_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; @@ -924,21 +1339,21 @@ static int loopback_format_info(struct s uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST; uinfo->value.integer.step = 1; return 0; -} +} static int loopback_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct loopback *loopback = snd_kcontrol_chip(kcontrol); - + ucontrol->value.integer.value[0] = loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].format; return 0; } -static int loopback_rate_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int loopback_rate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; @@ -946,23 +1361,21 @@ static int loopback_rate_info(struct snd uinfo->value.integer.max = 192000; uinfo->value.integer.step = 1; return 0; -} +} static int loopback_rate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct loopback *loopback = snd_kcontrol_chip(kcontrol); - - mutex_lock(&loopback->cable_lock); + ucontrol->value.integer.value[0] = loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].rate; - mutex_unlock(&loopback->cable_lock); return 0; } -static int loopback_channels_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int loopback_channels_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; @@ -970,21 +1383,164 @@ static int loopback_channels_info(struct uinfo->value.integer.max = 1024; uinfo->value.integer.step = 1; return 0; -} +} static int loopback_channels_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct loopback *loopback = snd_kcontrol_chip(kcontrol); - - mutex_lock(&loopback->cable_lock); + ucontrol->value.integer.value[0] = loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].channels; + return 0; +} + +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC +static int loopback_sync_pcm_rate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 8000; + uinfo->value.integer.max = 192000; + uinfo->value.integer.step = 1; + return 0; +} + +static int loopback_sync_pcm_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].sync_pcm.rate; + return 0; +} + +static int loopback_sync_pcm_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + + val = ucontrol->value.integer.value[0]; + if (val < 8000) + val = 8000; + if (val > 192000) + val = 192000; + mutex_lock(&loopback->cable_lock); + if (val != loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].sync_pcm.rate) { + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].sync_pcm.rate = val; + change = 1; + } + mutex_unlock(&loopback->cable_lock); + return change; +} + +static int loopback_sync_pcm_channels_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 1; + uinfo->value.integer.max = 1024; + uinfo->value.integer.step = 1; + return 0; +} + +static int loopback_sync_pcm_channels_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].sync_pcm.channels; + return 0; +} + +static int loopback_sync_pcm_channels_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + + val = ucontrol->value.integer.value[0]; + if (val < 1) + val = 1; + if (val > 1024) + val = 1024; + mutex_lock(&loopback->cable_lock); + if (val != loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].sync_pcm.channels) { + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].sync_pcm.channels = val; + change = 1; + } mutex_unlock(&loopback->cable_lock); + return change; +} + +static int loopback_sync_pcm_stream_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct loopback_kcontrol_enum *e = + (struct loopback_kcontrol_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = e->items; + + if (uinfo->value.enumerated.item > e->items - 1) + uinfo->value.enumerated.item = e->items - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + return 0; +} + +static int loopback_sync_pcm_stream_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].sync_pcm.stream; + return 0; } +static int loopback_sync_pcm_stream_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + struct loopback_kcontrol_enum *e = + (struct loopback_kcontrol_enum *)kcontrol->private_value; + unsigned int val; + int change = 0; + + if (ucontrol->value.enumerated.item[0] > e->items - 1) + return -EINVAL; + val = ucontrol->value.enumerated.item[0]; + + mutex_lock(&loopback->cable_lock); + if (val != loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].sync_pcm.stream) { + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].sync_pcm.stream = val; + change = 1; + } + mutex_unlock(&loopback->cable_lock); + return change; +} +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ + static struct snd_kcontrol_new loopback_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -1031,7 +1587,31 @@ static struct snd_kcontrol_new loopback_ .name = "PCM Slave Channels", .info = loopback_channels_info, .get = loopback_channels_get -} +}, +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Sync PCM Rate", + .info = loopback_sync_pcm_rate_info, + .get = loopback_sync_pcm_rate_get, + .put = loopback_sync_pcm_rate_put, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Sync PCM Channels", + .info = loopback_sync_pcm_channels_info, + .get = loopback_sync_pcm_channels_get, + .put = loopback_sync_pcm_channels_put, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Sync PCM Stream", + .info = loopback_sync_pcm_stream_info, + .get = loopback_sync_pcm_stream_get, + .put = loopback_sync_pcm_stream_put, + .private_value = (unsigned long)&loopback_sync_pcm_stream_enum, +}, +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ }; static int loopback_mixer_new(struct loopback *loopback, int notify) @@ -1054,6 +1634,11 @@ static int loopback_mixer_new(struct loo setup->format = SNDRV_PCM_FORMAT_S16_LE; setup->rate = 48000; setup->channels = 2; +#ifdef CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC + setup->sync_pcm.rate = SYNC_PCM_DEFAULT_RATE; + setup->sync_pcm.channels = SYNC_PCM_DEFAULT_CHANNELS; + setup->sync_pcm.stream = SYNC_PCM_DEFAULT_STREAM; +#endif /* CONFIG_SND_ALOOP_RUN_SYNC_WITH_OTHER_CODEC */ for (idx = 0; idx < ARRAY_SIZE(loopback_controls); idx++) { kctl = snd_ctl_new1(&loopback_controls[idx], @@ -1106,10 +1691,13 @@ static void print_dpcm_info(struct snd_i snd_iprintf(buffer, " update_pending:\t%u\n", dpcm->period_update_pending); snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); +#ifdef SND_ALOOP_USE_HRTIMER +#else /* SND_ALOOP_USE_HRTIMER */ snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", dpcm->last_jiffies, jiffies); snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); +#endif /* SND_ALOOP_USE_HRTIMER */ } static void print_substream_info(struct snd_info_buffer *buffer, @@ -1177,7 +1765,7 @@ static int loopback_probe(struct platfor pcm_substreams[dev] = 1; if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; - + loopback->card = card; mutex_init(&loopback->cable_lock); @@ -1223,7 +1811,7 @@ static int loopback_suspend(struct devic snd_pcm_suspend_all(loopback->pcm[1]); return 0; } - + static int loopback_resume(struct device *pdev) { struct snd_card *card = dev_get_drvdata(pdev);