summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2015-03-25 13:14:25 +0000
committerhselasky <hselasky@FreeBSD.org>2015-03-25 13:14:25 +0000
commit9c325f67a8ec98bb5373baef3fbea78e5d25b272 (patch)
treea719fe3dd1bc34248ffade2ec38e0f23fba41495 /sys/dev/sound
parent9265dbcd9cb7ee74388547efbd2157cdf50566b3 (diff)
downloadFreeBSD-src-9c325f67a8ec98bb5373baef3fbea78e5d25b272.zip
FreeBSD-src-9c325f67a8ec98bb5373baef3fbea78e5d25b272.tar.gz
MFC r280322 and r280429:
The synchronisation value returned by the so-called feedback endpoint appears to be too inaccurate that it can be used to synchronize the playback data stream. If there is a recording endpoint associated with the playback endpoint, use that instead. That means if the isochronous OUT endpoint is asynchronus the USB audio driver will automatically start recording, if possible, to get exact information about the needed sample rate adjustments. In no recording endpoint is present, no rate adaption will be done. While at it fix an issue where the hardware buffer pointers don't get reset at the first device PCM trigger. Make some variables 32-bit to avoid problems with multithreading. Use the feedback value from the synchronization endpoint as fallback when there is no recording channel. PR: 198444
Diffstat (limited to 'sys/dev/sound')
-rw-r--r--sys/dev/sound/usb/uaudio.c393
-rw-r--r--sys/dev/sound/usb/uaudio.h4
-rw-r--r--sys/dev/sound/usb/uaudio_pcm.c14
3 files changed, 282 insertions, 129 deletions
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index ae93983..a6e8e6e 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -218,26 +218,25 @@ struct uaudio_chan {
uint32_t sample_rem;
uint32_t sample_curr;
uint32_t max_buf;
+ int32_t jitter_rem;
+ int32_t jitter_curr;
+
+ int feedback_rate;
uint32_t pcm_format[2];
uint16_t bytes_per_frame[2];
- uint8_t num_alt;
- uint8_t cur_alt;
- uint8_t set_alt;
- uint8_t operation;
+ uint32_t intr_counter;
+ uint32_t running;
+ uint32_t num_alt;
+ uint32_t cur_alt;
+ uint32_t set_alt;
+ uint32_t operation;
#define CHAN_OP_NONE 0
#define CHAN_OP_START 1
#define CHAN_OP_STOP 2
#define CHAN_OP_DRAIN 3
-
- /* USB audio feedback endpoint state */
- struct {
- uint16_t time; /* I/O interrupt count */
- int16_t constant; /* sample rate adjustment in Hz */
- int16_t remainder; /* current remainder */
- } feedback;
};
#define UMIDI_EMB_JACK_MAX 16 /* units */
@@ -1096,6 +1095,11 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas
uaudio_mixer_register_sysctl(sc, dev);
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "feedback_rate", CTLFLAG_RD, &sc->sc_play_chan.feedback_rate,
+ 0, "Feedback sample rate in Hz");
+
return (0); /* success */
detach:
@@ -1294,7 +1298,6 @@ uaudio_configure_msg_sub(struct uaudio_softc *sc,
chan->frames_per_second = fps;
chan->sample_rem = chan_alt->sample_rate % fps;
chan->sample_curr = 0;
- chan->frames_per_second = fps;
/* compute required buffer size */
buf_size = (chan->bytes_per_frame[1] * frames);
@@ -1974,7 +1977,7 @@ uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct uaudio_chan *ch = usbd_xfer_softc(xfer);
struct usb_page_cache *pc;
- uint64_t sample_rate = ch->usb_alt[ch->cur_alt].sample_rate;
+ uint64_t sample_rate;
uint8_t buf[4];
uint64_t temp;
int len;
@@ -2017,6 +2020,8 @@ uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
temp *= 125ULL;
+ sample_rate = ch->usb_alt[ch->cur_alt].sample_rate;
+
/* auto adjust */
while (temp < (sample_rate - (sample_rate / 4)))
temp *= 2;
@@ -2024,35 +2029,17 @@ uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
while (temp > (sample_rate + (sample_rate / 2)))
temp /= 2;
- /*
- * Some USB audio devices only report a sample rate
- * different from the nominal one when they want one
- * more or less sample. Make sure we catch this case
- * by pulling the sample rate offset slowly towards
- * zero if the reported value is equal to the sample
- * rate.
- */
- if (temp > sample_rate)
- ch->feedback.constant += 1;
- else if (temp < sample_rate)
- ch->feedback.constant -= 1;
- else if (ch->feedback.constant > 0)
- ch->feedback.constant--;
- else if (ch->feedback.constant < 0)
- ch->feedback.constant++;
-
- DPRINTF("Comparing %d Hz :: %d Hz :: %d samples drift\n",
- (int)temp, (int)sample_rate, (int)ch->feedback.constant);
+ DPRINTF("Comparing %d Hz :: %d Hz\n",
+ (int)temp, (int)sample_rate);
/*
- * Range check sync constant. We cannot change the
- * number of samples per second by more than the value
- * defined by "UAUDIO_IRQS":
+ * Use feedback value as fallback when there is no
+ * recording channel:
*/
- if (ch->feedback.constant > UAUDIO_IRQS)
- ch->feedback.constant = UAUDIO_IRQS;
- else if (ch->feedback.constant < -UAUDIO_IRQS)
- ch->feedback.constant = -UAUDIO_IRQS;
+ if (ch->priv_sc->sc_rec_chan.num_alt == 0)
+ ch->jitter_curr = temp - sample_rate;
+
+ ch->feedback_rate = temp;
break;
case USB_ST_SETUP:
@@ -2066,43 +2053,98 @@ uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
}
}
+static int
+uaudio_chan_is_async(struct uaudio_chan *ch, uint8_t alt)
+{
+ uint8_t attr = ch->usb_alt[alt].p_ed1->bmAttributes;
+ return (UE_GET_ISO_TYPE(attr) == UE_ISO_ASYNC);
+}
+
static void
uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct uaudio_chan *ch = usbd_xfer_softc(xfer);
+ struct uaudio_chan *ch_rec;
struct usb_page_cache *pc;
- uint32_t sample_size = ch->usb_alt[ch->cur_alt].sample_size;
uint32_t mfl;
uint32_t total;
uint32_t blockcount;
uint32_t n;
uint32_t offset;
+ int sample_size;
int actlen;
int sumlen;
- usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
-
- if (ch->end == ch->start) {
- DPRINTF("no buffer!\n");
+ if (ch->running == 0 || ch->start == ch->end) {
+ DPRINTF("not running or no buffer!\n");
return;
}
+ /* check if there is a record channel */
+ if (ch->priv_sc->sc_rec_chan.num_alt > 0)
+ ch_rec = &ch->priv_sc->sc_rec_chan;
+ else
+ ch_rec = NULL;
+
+ usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
+
switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+tr_setup:
+ if (ch_rec != NULL) {
+ /* reset receive jitter counters */
+ mtx_lock(ch_rec->pcm_mtx);
+ ch_rec->jitter_curr = 0;
+ ch_rec->jitter_rem = 0;
+ mtx_unlock(ch_rec->pcm_mtx);
+ }
+
+ /* reset transmit jitter counters */
+ ch->jitter_curr = 0;
+ ch->jitter_rem = 0;
+
+ /* FALLTHROUGH */
case USB_ST_TRANSFERRED:
-tr_transferred:
if (actlen < sumlen) {
DPRINTF("short transfer, "
"%d of %d bytes\n", actlen, sumlen);
}
chn_intr(ch->pcm_ch);
+ /*
+ * Check for asynchronous playback endpoint and that
+ * the playback endpoint is properly configured:
+ */
+ if (ch_rec != NULL &&
+ uaudio_chan_is_async(ch, ch->cur_alt) != 0) {
+ mtx_lock(ch_rec->pcm_mtx);
+ if (ch_rec->cur_alt < ch_rec->num_alt) {
+ int64_t tx_jitter;
+ int64_t rx_rate;
+
+ /* translate receive jitter into transmit jitter */
+ tx_jitter = ch->usb_alt[ch->cur_alt].sample_rate;
+ tx_jitter = (tx_jitter * ch_rec->jitter_curr) +
+ ch->jitter_rem;
+
+ /* reset receive jitter counters */
+ ch_rec->jitter_curr = 0;
+ ch_rec->jitter_rem = 0;
+
+ /* compute exact number of transmit jitter samples */
+ rx_rate = ch_rec->usb_alt[ch_rec->cur_alt].sample_rate;
+ ch->jitter_curr += tx_jitter / rx_rate;
+ ch->jitter_rem = tx_jitter % rx_rate;
+ }
+ mtx_unlock(ch_rec->pcm_mtx);
+ }
+
/* start the SYNC transfer one time per second, if any */
- if (++(ch->feedback.time) >= UAUDIO_IRQS) {
- ch->feedback.time = 0;
+ if (++(ch->intr_counter) >= UAUDIO_IRQS) {
+ ch->intr_counter = 0;
usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]);
}
- case USB_ST_SETUP:
mfl = usbd_xfer_max_framelen(xfer);
if (ch->bytes_per_frame[1] > mfl) {
@@ -2118,6 +2160,9 @@ tr_transferred:
/* setup number of frames */
usbd_xfer_set_frames(xfer, blockcount);
+ /* get sample size */
+ sample_size = ch->usb_alt[ch->cur_alt].sample_size;
+
/* reset total length */
total = 0;
@@ -2133,31 +2178,23 @@ tr_transferred:
frame_len = ch->bytes_per_frame[0];
}
- if (n == (blockcount - 1)) {
- /*
- * Update sync remainder and check if
- * we should transmit more or less
- * data:
- */
- ch->feedback.remainder += ch->feedback.constant;
- if (ch->feedback.remainder >= UAUDIO_IRQS) {
- ch->feedback.remainder -= UAUDIO_IRQS;
- DPRINTFN(6, "sending one sample more\n");
- if ((frame_len + sample_size) <= mfl)
- frame_len += sample_size;
- } else if (ch->feedback.remainder <= -UAUDIO_IRQS) {
- ch->feedback.remainder += UAUDIO_IRQS;
- DPRINTFN(6, "sending one sample less\n");
- if (frame_len >= sample_size)
- frame_len -= sample_size;
- }
+ /* handle free running clock case */
+ if (ch->jitter_curr > 0 &&
+ (frame_len + sample_size) <= mfl) {
+ DPRINTFN(6, "sending one sample more\n");
+ ch->jitter_curr--;
+ frame_len += sample_size;
+ } else if (ch->jitter_curr < 0 &&
+ frame_len >= sample_size) {
+ DPRINTFN(6, "sending one sample less\n");
+ ch->jitter_curr++;
+ frame_len -= sample_size;
}
-
usbd_xfer_set_frame_len(xfer, n, frame_len);
total += frame_len;
}
- DPRINTFN(6, "transfer %d bytes\n", total);
+ DPRINTFN(6, "transferring %d bytes\n", total);
offset = 0;
@@ -2165,28 +2202,25 @@ tr_transferred:
while (total > 0) {
n = (ch->end - ch->cur);
- if (n > total) {
+ if (n > total)
n = total;
- }
+
usbd_copy_in(pc, offset, ch->cur, n);
total -= n;
ch->cur += n;
offset += n;
- if (ch->cur >= ch->end) {
+ if (ch->cur >= ch->end)
ch->cur = ch->start;
- }
}
-
usbd_transfer_submit(xfer);
break;
default: /* Error */
- if (error == USB_ERR_CANCELLED) {
- break;
- }
- goto tr_transferred;
+ if (error != USB_ERR_CANCELLED)
+ goto tr_setup;
+ break;
}
}
@@ -2202,36 +2236,59 @@ uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
struct uaudio_chan *ch = usbd_xfer_softc(xfer);
struct usb_page_cache *pc;
uint32_t offset0;
- uint32_t offset1;
uint32_t mfl;
int m;
int n;
int len;
int actlen;
int nframes;
- int blockcount;
+ int expected_bytes;
+ int sample_size;
- usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
- mfl = usbd_xfer_max_framelen(xfer);
-
- if (ch->end == ch->start) {
+ if (ch->start == ch->end) {
DPRINTF("no buffer!\n");
return;
}
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
+ mfl = usbd_xfer_max_framelen(xfer);
+
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
- DPRINTFN(6, "transferred %d bytes\n", actlen);
-
offset0 = 0;
pc = usbd_xfer_get_frame(xfer, 0);
+ /* try to compute the number of expected bytes */
+ ch->sample_curr += (ch->sample_rem * ch->intr_frames);
+
+ /* compute number of expected bytes */
+ expected_bytes = (ch->intr_frames * ch->bytes_per_frame[0]) +
+ ((ch->sample_curr / ch->frames_per_second) *
+ (ch->bytes_per_frame[1] - ch->bytes_per_frame[0]));
+
+ /* keep remainder */
+ ch->sample_curr %= ch->frames_per_second;
+
+ /* get current sample size */
+ sample_size = ch->usb_alt[ch->cur_alt].sample_size;
+
for (n = 0; n != nframes; n++) {
+ uint32_t offset1 = offset0;
- offset1 = offset0;
len = usbd_xfer_frame_len(xfer, n);
+ /* make sure we only receive complete samples */
+ len = len - (len % sample_size);
+
+ /* subtract bytes received from expected payload */
+ expected_bytes -= len;
+
+ /* don't receive data when not ready */
+ if (ch->running == 0 || ch->cur_alt != ch->set_alt)
+ continue;
+
+ /* fill ring buffer with samples, if any */
while (len > 0) {
m = (ch->end - ch->cur);
@@ -2245,33 +2302,46 @@ uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
offset1 += m;
ch->cur += m;
- if (ch->cur >= ch->end) {
+ if (ch->cur >= ch->end)
ch->cur = ch->start;
- }
}
offset0 += mfl;
}
- chn_intr(ch->pcm_ch);
+ /* update current jitter */
+ ch->jitter_curr -= (expected_bytes / sample_size);
+
+ /* don't allow a huge amount of jitter to accumulate */
+ nframes = 2 * ch->intr_frames;
+
+ /* range check current jitter */
+ if (ch->jitter_curr < -nframes)
+ ch->jitter_curr = -nframes;
+ else if (ch->jitter_curr > nframes)
+ ch->jitter_curr = nframes;
+
+ DPRINTFN(6, "transferred %d bytes, jitter %d samples\n",
+ actlen, ch->jitter_curr);
+
+ if (ch->running != 0)
+ chn_intr(ch->pcm_ch);
case USB_ST_SETUP:
tr_setup:
- blockcount = ch->intr_frames;
+ nframes = ch->intr_frames;
- usbd_xfer_set_frames(xfer, blockcount);
- for (n = 0; n < blockcount; n++) {
+ usbd_xfer_set_frames(xfer, nframes);
+ for (n = 0; n != nframes; n++)
usbd_xfer_set_frame_len(xfer, n, mfl);
- }
usbd_transfer_submit(xfer);
break;
default: /* Error */
- if (error == USB_ERR_CANCELLED) {
- break;
- }
- goto tr_setup;
+ if (error != USB_ERR_CANCELLED)
+ goto tr_setup;
+ break;
}
}
@@ -2344,13 +2414,7 @@ int
uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
{
uint32_t temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
-
sndbuf_setup(ch->pcm_buf, ch->buf, temp);
-
- ch->start = ch->buf;
- ch->end = ch->buf + temp;
- ch->cur = ch->buf;
-
return (temp / 2);
}
@@ -2364,8 +2428,11 @@ uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize,
int
uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed)
{
+ struct uaudio_softc *sc;
uint8_t x;
+ sc = ch->priv_sc;
+
for (x = 0; x < ch->num_alt; x++) {
if (ch->usb_alt[x].sample_rate < speed) {
/* sample rate is too low */
@@ -2376,7 +2443,9 @@ uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed)
if (x != 0)
x--;
+ usb_proc_explore_lock(sc->sc_udev);
ch->set_alt = x;
+ usb_proc_explore_unlock(sc->sc_udev);
DPRINTF("Selecting alt %d\n", (int)x);
@@ -2447,16 +2516,16 @@ uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format)
return (0);
}
-int
-uaudio_chan_start(struct uaudio_chan *ch)
+static void
+uaudio_chan_start_sub(struct uaudio_chan *ch)
{
struct uaudio_softc *sc = ch->priv_sc;
int do_start = 0;
- usb_proc_explore_lock(sc->sc_udev);
if (ch->operation != CHAN_OP_DRAIN) {
if (ch->cur_alt == ch->set_alt &&
- ch->operation == CHAN_OP_NONE) {
+ ch->operation == CHAN_OP_NONE &&
+ mtx_owned(ch->pcm_mtx) != 0) {
/* save doing the explore task */
do_start = 1;
} else {
@@ -2465,28 +2534,81 @@ uaudio_chan_start(struct uaudio_chan *ch)
&sc->sc_config_msg[0], &sc->sc_config_msg[1]);
}
}
- usb_proc_explore_unlock(sc->sc_udev);
-
- /* reset feedback endpoint state */
- memset(&ch->feedback, 0, sizeof(ch->feedback));
-
if (do_start) {
usbd_transfer_start(ch->xfer[0]);
usbd_transfer_start(ch->xfer[1]);
}
- return (0);
}
-int
-uaudio_chan_stop(struct uaudio_chan *ch)
+static int
+uaudio_chan_need_both(struct uaudio_softc *sc)
+{
+ return (sc->sc_play_chan.num_alt > 0 &&
+ sc->sc_play_chan.running != 0 &&
+ uaudio_chan_is_async(&sc->sc_play_chan,
+ sc->sc_play_chan.set_alt) != 0 &&
+ sc->sc_rec_chan.num_alt > 0 &&
+ sc->sc_rec_chan.running == 0);
+}
+
+static int
+uaudio_chan_need_none(struct uaudio_softc *sc)
+{
+ return (sc->sc_play_chan.num_alt > 0 &&
+ sc->sc_play_chan.running == 0 &&
+ sc->sc_rec_chan.num_alt > 0 &&
+ sc->sc_rec_chan.running == 0);
+}
+
+void
+uaudio_chan_start(struct uaudio_chan *ch)
{
struct uaudio_softc *sc = ch->priv_sc;
- int do_stop = 0;
+ /* make operation atomic */
usb_proc_explore_lock(sc->sc_udev);
+
+ /* check if not running */
+ if (ch->running == 0) {
+ uint32_t temp;
+
+ /* get current buffer size */
+ temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
+
+ /* set running flag */
+ ch->running = 1;
+
+ /* ensure the hardware buffer is reset */
+ ch->start = ch->buf;
+ ch->end = ch->buf + temp;
+ ch->cur = ch->buf;
+
+ if (uaudio_chan_need_both(sc)) {
+ /*
+ * Start both endpoints because of need for
+ * jitter information:
+ */
+ uaudio_chan_start_sub(&sc->sc_rec_chan);
+ uaudio_chan_start_sub(&sc->sc_play_chan);
+ } else {
+ uaudio_chan_start_sub(ch);
+ }
+ }
+
+ /* exit atomic operation */
+ usb_proc_explore_unlock(sc->sc_udev);
+}
+
+static void
+uaudio_chan_stop_sub(struct uaudio_chan *ch)
+{
+ struct uaudio_softc *sc = ch->priv_sc;
+ int do_stop = 0;
+
if (ch->operation != CHAN_OP_DRAIN) {
if (ch->cur_alt == ch->set_alt &&
- ch->operation == CHAN_OP_NONE) {
+ ch->operation == CHAN_OP_NONE &&
+ mtx_owned(ch->pcm_mtx) != 0) {
/* save doing the explore task */
do_stop = 1;
} else {
@@ -2495,13 +2617,44 @@ uaudio_chan_stop(struct uaudio_chan *ch)
&sc->sc_config_msg[0], &sc->sc_config_msg[1]);
}
}
- usb_proc_explore_unlock(sc->sc_udev);
-
if (do_stop) {
usbd_transfer_stop(ch->xfer[0]);
usbd_transfer_stop(ch->xfer[1]);
}
- return (0);
+}
+
+void
+uaudio_chan_stop(struct uaudio_chan *ch)
+{
+ struct uaudio_softc *sc = ch->priv_sc;
+
+ /* make operation atomic */
+ usb_proc_explore_lock(sc->sc_udev);
+
+ /* check if running */
+ if (ch->running != 0) {
+ /* clear running flag */
+ ch->running = 0;
+
+ if (uaudio_chan_need_both(sc)) {
+ /*
+ * Leave the endpoints running because we need
+ * information about jitter!
+ */
+ } else if (uaudio_chan_need_none(sc)) {
+ /*
+ * Stop both endpoints in case the one was used for
+ * jitter information:
+ */
+ uaudio_chan_stop_sub(&sc->sc_rec_chan);
+ uaudio_chan_stop_sub(&sc->sc_play_chan);
+ } else {
+ uaudio_chan_stop_sub(ch);
+ }
+ }
+
+ /* exit atomic operation */
+ usb_proc_explore_unlock(sc->sc_udev);
}
/*========================================================================*
diff --git a/sys/dev/sound/usb/uaudio.h b/sys/dev/sound/usb/uaudio.h
index 10c10de..2d9a875 100644
--- a/sys/dev/sound/usb/uaudio.h
+++ b/sys/dev/sound/usb/uaudio.h
@@ -54,8 +54,8 @@ extern struct pcmchan_matrix *uaudio_chan_getmatrix(struct uaudio_chan *ch,
uint32_t format);
extern int uaudio_chan_set_param_format(struct uaudio_chan *ch,
uint32_t format);
-extern int uaudio_chan_start(struct uaudio_chan *ch);
-extern int uaudio_chan_stop(struct uaudio_chan *ch);
+extern void uaudio_chan_start(struct uaudio_chan *ch);
+extern void uaudio_chan_stop(struct uaudio_chan *ch);
extern int uaudio_mixer_init_sub(struct uaudio_softc *sc,
struct snd_mixer *m);
extern int uaudio_mixer_uninit_sub(struct uaudio_softc *sc);
diff --git a/sys/dev/sound/usb/uaudio_pcm.c b/sys/dev/sound/usb/uaudio_pcm.c
index bd00a01..10e8cc4 100644
--- a/sys/dev/sound/usb/uaudio_pcm.c
+++ b/sys/dev/sound/usb/uaudio_pcm.c
@@ -81,14 +81,14 @@ ua_chan_setfragments(kobj_t obj, void *data, uint32_t blocksize, uint32_t blockc
static int
ua_chan_trigger(kobj_t obj, void *data, int go)
{
- if (!PCMTRIG_COMMON(go)) {
- return (0);
- }
- if (go == PCMTRIG_START) {
- return (uaudio_chan_start(data));
- } else {
- return (uaudio_chan_stop(data));
+ if (PCMTRIG_COMMON(go)) {
+ if (go == PCMTRIG_START) {
+ uaudio_chan_start(data);
+ } else {
+ uaudio_chan_stop(data);
+ }
}
+ return (0);
}
static uint32_t
OpenPOWER on IntegriCloud