diff mbox series

[RFC,10/15] ALSA: usb: Export PCM operations

Message ID 20250409110731.3752332-11-cezary.rojewski@intel.com (mailing list archive)
State RFC
Headers show
Series ALSA/ASoC: USB Audio Offload | expand

Commit Message

Cezary Rojewski April 9, 2025, 11:07 a.m. UTC
Existing PCM operations can be reused by DAIs on the ASoC side. Not all
of them are exported as endpoints of the offloaded USB device are
entirely controlled by the Audio Sideband and AudioDSP hardware. As ASoC
occupies substream->private_data, update existing open() and close() to
utilize runtime->private_data for obtaining USB stream instead.

Provide a wrapper for ASoC driver i.e.: snd_usb_pcm_open() as they are
unaware of details of USB streams and do not access USB substreams
directly.

Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 include/sound/usb.h |   9 +++
 sound/usb/pcm.c     | 159 +++++++++++++++++++++++++++++++-------------
 2 files changed, 120 insertions(+), 48 deletions(-)
diff mbox series

Patch

diff --git a/include/sound/usb.h b/include/sound/usb.h
index f30a8a96c49e..daba868fb6e0 100644
--- a/include/sound/usb.h
+++ b/include/sound/usb.h
@@ -93,6 +93,15 @@  struct snd_usb_audio {
 int snd_usb_bind_card(struct snd_usb_audio *chip, struct snd_card *card,
 		      struct usb_driver *driver);
 int snd_usb_bind_pcm(struct list_head *stream_entry, struct snd_pcm *pcm);
+void snd_usb_pcm_hw_init(struct list_head *stream_entry, int dir, struct snd_pcm_hardware *hw);
+
+/* PCM operations, see struct snd_pcm_ops. */
+int snd_usb_pcm_open(struct snd_pcm_substream *substream);
+int snd_usb_pcm_close(struct snd_pcm_substream *substream);
+int snd_usb_pcm_hw_params(struct snd_pcm_substream *substream,
+			  struct snd_pcm_hw_params *hw_params);
+int snd_usb_pcm_hw_free(struct snd_pcm_substream *substream);
+int snd_usb_pcm_prepare(struct snd_pcm_substream *substream);
 
 #define USB_AUDIO_IFACE_UNUSED	((void *)-1L)
 
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index ea698f061af9..e04e47c0a35e 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -471,8 +471,8 @@  static void close_endpoints(struct snd_usb_audio *chip,
  * if sg buffer is supported on the later version of alsa, we'll follow
  * that.
  */
-static int snd_usb_hw_params(struct snd_pcm_substream *substream,
-			     struct snd_pcm_hw_params *hw_params)
+int snd_usb_pcm_hw_params(struct snd_pcm_substream *substream,
+			  struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_usb_substream *subs = substream->runtime->private_data;
 	struct snd_usb_audio *chip = subs->stream->chip;
@@ -579,13 +579,14 @@  static int snd_usb_hw_params(struct snd_pcm_substream *substream,
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(snd_usb_pcm_hw_params);
 
 /*
  * hw_free callback
  *
  * reset the audio format and release the buffer
  */
-static int snd_usb_hw_free(struct snd_pcm_substream *substream)
+int snd_usb_pcm_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_usb_substream *subs = substream->runtime->private_data;
 	struct snd_usb_audio *chip = subs->stream->chip;
@@ -603,6 +604,7 @@  static int snd_usb_hw_free(struct snd_pcm_substream *substream)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_usb_pcm_hw_free);
 
 /* free-wheeling mode? (e.g. dmix) */
 static int in_free_wheeling_mode(struct snd_pcm_runtime *runtime)
@@ -634,7 +636,7 @@  static int lowlatency_playback_available(struct snd_pcm_runtime *runtime,
  *
  * only a few subtle things...
  */
-static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
+int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_usb_substream *subs = runtime->private_data;
@@ -693,6 +695,7 @@  static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
 	snd_usb_unlock_shutdown(chip);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(snd_usb_pcm_prepare);
 
 /*
  * h/w constraints
@@ -1075,6 +1078,47 @@  static int hw_rule_periods_implicit_fb(struct snd_pcm_hw_params *params,
 	return apply_hw_params_minmax(it, rmin, rmax);
 }
 
+static void usb_pcm_hw_init(struct snd_usb_substream *subs, struct snd_pcm_hardware *hw)
+{
+	const struct audioformat *fp;
+
+	hw->formats = subs->formats;
+	hw->rate_min = 0x7fffffff;
+	hw->rate_max = 0;
+	hw->channels_min = 256;
+	hw->channels_max = 0;
+	hw->rates = 0;
+
+	/* Now reduce the scope based on substream capabilities. */
+	list_for_each_entry(fp, &subs->fmt_list, list) {
+		hw->rates |= fp->rates;
+		if (hw->rate_min > fp->rate_min)
+			hw->rate_min = fp->rate_min;
+		if (hw->rate_max < fp->rate_max)
+			hw->rate_max = fp->rate_max;
+		if (hw->channels_min > fp->channels)
+			hw->channels_min = fp->channels;
+		if (hw->channels_max < fp->channels)
+			hw->channels_max = fp->channels;
+		if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) {
+			/* FIXME: there might be more than one audio formats... */
+			hw->period_bytes_min = fp->frame_size;
+			hw->period_bytes_max = fp->frame_size;
+		}
+	}
+}
+
+void snd_usb_pcm_hw_init(struct list_head *stream_entry, int dir, struct snd_pcm_hardware *hw)
+{
+	struct snd_usb_stream *as;
+
+	if (dir <= SNDRV_PCM_STREAM_LAST) {
+		as = list_entry(stream_entry, struct snd_usb_stream, list);
+		usb_pcm_hw_init(&as->substream[dir], hw);
+	}
+}
+EXPORT_SYMBOL_GPL(snd_usb_pcm_hw_init);
+
 /*
  * set up the runtime hardware information.
  */
@@ -1086,30 +1130,10 @@  static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
 	int param_period_time_if_needed = -1;
 	int err;
 
-	runtime->hw.formats = subs->formats;
-
-	runtime->hw.rate_min = 0x7fffffff;
-	runtime->hw.rate_max = 0;
-	runtime->hw.channels_min = 256;
-	runtime->hw.channels_max = 0;
-	runtime->hw.rates = 0;
 	ptmin = UINT_MAX;
-	/* check min/max rates and channels */
+	usb_pcm_hw_init(subs, &runtime->hw);
+
 	list_for_each_entry(fp, &subs->fmt_list, list) {
-		runtime->hw.rates |= fp->rates;
-		if (runtime->hw.rate_min > fp->rate_min)
-			runtime->hw.rate_min = fp->rate_min;
-		if (runtime->hw.rate_max < fp->rate_max)
-			runtime->hw.rate_max = fp->rate_max;
-		if (runtime->hw.channels_min > fp->channels)
-			runtime->hw.channels_min = fp->channels;
-		if (runtime->hw.channels_max < fp->channels)
-			runtime->hw.channels_max = fp->channels;
-		if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) {
-			/* FIXME: there might be more than one audio formats... */
-			runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
-				fp->frame_size;
-		}
 		pt = 125 * (1 << fp->datainterval);
 		ptmin = min(ptmin, pt);
 	}
@@ -1202,12 +1226,12 @@  static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
 	return 0;
 }
 
-static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
+static int __usb_pcm_open(struct snd_pcm_substream *substream)
 {
-	int direction = substream->stream;
-	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_usb_substream *subs = &as->substream[direction];
+	struct snd_usb_substream *subs = runtime->private_data;
+	struct snd_usb_stream *as = subs->stream;
+	int direction = substream->stream;
 	int ret;
 
 	runtime->hw = snd_usb_hardware;
@@ -1215,7 +1239,6 @@  static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
 	if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
 	    as->chip->lowlatency)
 		runtime->hw.info |= SNDRV_PCM_INFO_SYNC_APPLPTR;
-	runtime->private_data = subs;
 	subs->pcm_substream = substream;
 	/* runtime PM is also done there */
 
@@ -1227,36 +1250,76 @@  static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
 	ret = setup_hw_info(runtime, subs);
 	if (ret < 0)
 		return ret;
-	ret = snd_usb_autoresume(subs->stream->chip);
+	ret = snd_usb_autoresume(as->chip);
 	if (ret < 0)
 		return ret;
-	ret = snd_media_stream_init(subs, as->pcm, direction);
+	ret = snd_media_stream_init(subs, substream->pcm, direction);
 	if (ret < 0)
-		snd_usb_autosuspend(subs->stream->chip);
+		snd_usb_autosuspend(as->chip);
 	return ret;
 }
 
-static int snd_usb_pcm_close(struct snd_pcm_substream *substream)
+static int usb_pcm_open(struct snd_pcm_substream *substream)
 {
-	int direction = substream->stream;
 	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
-	struct snd_usb_substream *subs = &as->substream[direction];
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	/*
+	 * On ASoC side, substream->private_data is occupied by the
+	 * framework.  For ALSA and ASoC to share USB PCM operations
+	 * runtime->private_data shall be utilized instead.
+	 */
+	runtime->private_data = &as->substream[substream->stream];
+
+	return __usb_pcm_open(substream);
+}
+
+int snd_usb_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_usb_stream *as;
+
+	/*
+	 * On ASoC side, substream->private_data is occupied by the
+	 * framework.  For ALSA and ASoC to share USB PCM operations
+	 * runtime->private_data shall be utilized instead.
+	 */
+	as = list_entry(runtime->private_data, struct snd_usb_stream, list);
+	runtime->private_data = &as->substream[substream->stream];
+
+	return __usb_pcm_open(substream);
+}
+EXPORT_SYMBOL_GPL(snd_usb_pcm_open);
+
+int snd_usb_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_usb_substream *subs = substream->runtime->private_data;
+	struct snd_usb_stream *as = subs->stream;
 	int ret;
 
 	snd_media_stop_pipeline(subs);
 
-	if (!snd_usb_lock_shutdown(subs->stream->chip)) {
+	if (!snd_usb_lock_shutdown(as->chip)) {
 		ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D1);
-		snd_usb_unlock_shutdown(subs->stream->chip);
+		snd_usb_unlock_shutdown(as->chip);
 		if (ret < 0)
 			return ret;
 	}
 
 	subs->pcm_substream = NULL;
-	snd_usb_autosuspend(subs->stream->chip);
+	snd_usb_autosuspend(as->chip);
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_usb_pcm_close);
+
+static int usb_pcm_close(struct snd_pcm_substream *substream)
+{
+	int ret = snd_usb_pcm_close(substream);
+
+	substream->runtime->private_data = NULL;
+	return ret;
+}
 
 /* Since a URB can handle only a single linear buffer, we must use double
  * buffering when the data to be transferred overflows the buffer boundary.
@@ -1744,10 +1807,10 @@  static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
 }
 
 static const struct snd_pcm_ops snd_usb_playback_ops = {
-	.open =		snd_usb_pcm_open,
-	.close =	snd_usb_pcm_close,
-	.hw_params =	snd_usb_hw_params,
-	.hw_free =	snd_usb_hw_free,
+	.open =		usb_pcm_open,
+	.close =	usb_pcm_close,
+	.hw_params =	snd_usb_pcm_hw_params,
+	.hw_free =	snd_usb_pcm_hw_free,
 	.prepare =	snd_usb_pcm_prepare,
 	.trigger =	snd_usb_substream_playback_trigger,
 	.sync_stop =	snd_usb_pcm_sync_stop,
@@ -1756,10 +1819,10 @@  static const struct snd_pcm_ops snd_usb_playback_ops = {
 };
 
 static const struct snd_pcm_ops snd_usb_capture_ops = {
-	.open =		snd_usb_pcm_open,
-	.close =	snd_usb_pcm_close,
-	.hw_params =	snd_usb_hw_params,
-	.hw_free =	snd_usb_hw_free,
+	.open =		usb_pcm_open,
+	.close =	usb_pcm_close,
+	.hw_params =	snd_usb_pcm_hw_params,
+	.hw_free =	snd_usb_pcm_hw_free,
 	.prepare =	snd_usb_pcm_prepare,
 	.trigger =	snd_usb_substream_capture_trigger,
 	.sync_stop =	snd_usb_pcm_sync_stop,