Linux Sound subsystem development
 help / color / mirror / Atom feed
From: Cezary Rojewski <cezary.rojewski@intel.com>
To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz
Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org,
	gregkh@linuxfoundation.org, quic_wcheng@quicinc.com,
	mathias.nyman@linux.intel.com,
	Cezary Rojewski <cezary.rojewski@intel.com>
Subject: [RFC 10/15] ALSA: usb: Export PCM operations
Date: Wed,  9 Apr 2025 13:07:25 +0200	[thread overview]
Message-ID: <20250409110731.3752332-11-cezary.rojewski@intel.com> (raw)
In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com>

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 --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,
-- 
2.25.1


  parent reply	other threads:[~2025-04-09 10:51 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-09 11:07 [RFC 00/15] ALSA/ASoC: USB Audio Offload Cezary Rojewski
2025-04-09 11:07 ` [RFC 01/15] ALSA: usb: Move media-filters to the media code Cezary Rojewski
2025-04-09 11:07 ` [RFC 02/15] ALSA: usb: Drop private_free() usage for card and pcms Cezary Rojewski
2025-04-09 11:07 ` [RFC 03/15] ALSA: usb: Relocate the usbaudio header file Cezary Rojewski
2025-04-09 11:07 ` [RFC 04/15] ALSA: usb: Implement two-stage quirk applying mechanism Cezary Rojewski
2025-04-09 11:07 ` [RFC 05/15] ALSA: usb: Implement two-stage stream creation mechanism Cezary Rojewski
2025-04-09 11:07 ` [RFC 06/15] ALSA: usb: Implement two-stage chip probing mechanism Cezary Rojewski
2025-04-09 11:07 ` [RFC 07/15] ALSA: usb: Switch to the two-stage chip probing Cezary Rojewski
2025-04-09 11:07 ` [RFC 08/15] ALSA: usb: Switch to the two-stage stream creation Cezary Rojewski
2025-04-09 11:07 ` [RFC 09/15] ALSA: usb: Switch to the two-stage quirk applying Cezary Rojewski
2025-04-09 11:07 ` Cezary Rojewski [this message]
2025-04-09 11:07 ` [RFC 11/15] ALSA: usb: Export usb_interface driver operations Cezary Rojewski
2025-04-09 11:07 ` [RFC 12/15] ALSA: usb: Export card-naming procedure Cezary Rojewski
2025-04-09 11:07 ` [RFC 13/15] ALSA: usb: Add getters to obtain endpoint information Cezary Rojewski
2025-04-09 11:07 ` [RFC 14/15] ASoC: codecs: Add USB-Audio driver Cezary Rojewski
2025-04-09 11:07 ` [RFC 15/15] ASoC: Intel: avs: Add USB machine board Cezary Rojewski
2025-04-09 12:10 ` [RFC 00/15] ALSA/ASoC: USB Audio Offload Greg KH
2025-04-09 13:06   ` Cezary Rojewski
2025-04-10 10:10     ` Takashi Iwai
2025-04-10 10:24 ` Takashi Iwai
2025-04-11  9:39   ` Cezary Rojewski
2025-04-15 16:15     ` Takashi Iwai
2025-04-17 10:15       ` Cezary Rojewski
2025-04-22 11:28         ` Pierre-Louis Bossart
2025-04-22 14:15           ` Cezary Rojewski
2025-04-25 16:53             ` Pierre-Louis Bossart
2025-04-11 14:04 ` Greg KH
2025-04-11 16:51   ` Cezary Rojewski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250409110731.3752332-11-cezary.rojewski@intel.com \
    --to=cezary.rojewski@intel.com \
    --cc=amadeuszx.slawinski@linux.intel.com \
    --cc=broonie@kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=mathias.nyman@linux.intel.com \
    --cc=perex@perex.cz \
    --cc=quic_wcheng@quicinc.com \
    --cc=tiwai@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox