mirror of https://github.com/OpenIPC/firmware.git
1251 lines
38 KiB
Diff
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,
|
|
+ ¤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);
|