firmware/br-ext-chip-allwinner/board/v83x/kernel/patches/00000-sound_drivers_aloop.c...

1251 lines
38 KiB
Diff

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 <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
@@ -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 <linux/hrtimer.h>
+#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,
+ &current_play_sample_cnt,
+ &current_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(&current_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);