]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: aoa: i2sbus: clear stale prepared state
authorCássio Gabriel <cassiogabrielcontato@gmail.com>
Tue, 31 Mar 2026 21:14:04 +0000 (18:14 -0300)
committerTakashi Iwai <tiwai@suse.de>
Wed, 1 Apr 2026 07:58:37 +0000 (09:58 +0200)
The i2sbus PCM code uses pi->active to constrain the sibling stream to
an already prepared duplex format and rate in i2sbus_pcm_open().

That state is set from i2sbus_pcm_prepare(), but the current code only
clears it on close. As a result, the sibling stream can inherit stale
constraints after the prepared state has been torn down.

Clear pi->active when hw_params() or hw_free() tears down the prepared
state, and set it again only after prepare succeeds.

Replace the stale FIXME in the duplex constraint comment with a description
of the current driver behavior: i2sbus still programs a single shared
transport configuration for both directions, so mixed formats are not
supported in duplex mode.

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202604010125.AvkWBYKI-lkp@intel.com/
Fixes: f3d9478b2ce4 ("[ALSA] snd-aoa: add snd-aoa")
Cc: stable@vger.kernel.org
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260331-aoa-i2sbus-clear-stale-active-v2-1-3764ae2889a1@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/aoa/soundbus/i2sbus/pcm.c

index 97c807e67d56812efc812ea3e237f4b49eb1e506..63004ece94f9333eaacdeb9ed2fb7298743d2e45 100644 (file)
@@ -165,17 +165,16 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
         * currently in use (if any). */
        hw->rate_min = 5512;
        hw->rate_max = 192000;
-       /* if the other stream is active, then we can only
-        * support what it is currently using.
-        * FIXME: I lied. This comment is wrong. We can support
-        * anything that works with the same serial format, ie.
-        * when recording 24 bit sound we can well play 16 bit
-        * sound at the same time iff using the same transfer mode.
+       /* If the other stream is already prepared, keep this stream
+        * on the same duplex format and rate.
+        *
+        * i2sbus_pcm_prepare() still programs one shared transport
+        * configuration for both directions, so mixed duplex formats
+        * are not supported here.
         */
        if (other->active) {
-               /* FIXME: is this guaranteed by the alsa api? */
                hw->formats &= pcm_format_to_bits(i2sdev->format);
-               /* see above, restrict rates to the one we already have */
+               /* Restrict rates to the one already in use. */
                hw->rate_min = i2sdev->rate;
                hw->rate_max = i2sdev->rate;
        }
@@ -283,6 +282,23 @@ void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev)
 }
 #endif
 
+static void i2sbus_pcm_clear_active(struct i2sbus_dev *i2sdev, int in)
+{
+       struct pcm_info *pi;
+
+       guard(mutex)(&i2sdev->lock);
+
+       get_pcm_info(i2sdev, in, &pi, NULL);
+       pi->active = 0;
+}
+
+static inline int i2sbus_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params, int in)
+{
+       i2sbus_pcm_clear_active(snd_pcm_substream_chip(substream), in);
+       return 0;
+}
+
 static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in)
 {
        struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
@@ -291,14 +307,27 @@ static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in)
        get_pcm_info(i2sdev, in, &pi, NULL);
        if (pi->dbdma_ring.stopping)
                i2sbus_wait_for_stop(i2sdev, pi);
+       i2sbus_pcm_clear_active(i2sdev, in);
        return 0;
 }
 
+static int i2sbus_playback_hw_params(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_hw_params *params)
+{
+       return i2sbus_hw_params(substream, params, 0);
+}
+
 static int i2sbus_playback_hw_free(struct snd_pcm_substream *substream)
 {
        return i2sbus_hw_free(substream, 0);
 }
 
+static int i2sbus_record_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params)
+{
+       return i2sbus_hw_params(substream, params, 1);
+}
+
 static int i2sbus_record_hw_free(struct snd_pcm_substream *substream)
 {
        return i2sbus_hw_free(substream, 1);
@@ -335,7 +364,6 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
                return -EINVAL;
 
        runtime = pi->substream->runtime;
-       pi->active = 1;
        if (other->active &&
            ((i2sdev->format != runtime->format)
             || (i2sdev->rate != runtime->rate)))
@@ -444,9 +472,11 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
 
        /* early exit if already programmed correctly */
        /* not locking these is fine since we touch them only in this function */
-       if (in_le32(&i2sdev->intfregs->serial_format) == sfr
-        && in_le32(&i2sdev->intfregs->data_word_sizes) == dws)
+       if (in_le32(&i2sdev->intfregs->serial_format) == sfr &&
+           in_le32(&i2sdev->intfregs->data_word_sizes) == dws) {
+               pi->active = 1;
                return 0;
+       }
 
        /* let's notify the codecs about clocks going away.
         * For now we only do mastering on the i2s cell... */
@@ -484,6 +514,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
                if (cii->codec->switch_clock)
                        cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE);
 
+       pi->active = 1;
        return 0;
 }
 
@@ -728,6 +759,7 @@ static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream
 static const struct snd_pcm_ops i2sbus_playback_ops = {
        .open =         i2sbus_playback_open,
        .close =        i2sbus_playback_close,
+       .hw_params =    i2sbus_playback_hw_params,
        .hw_free =      i2sbus_playback_hw_free,
        .prepare =      i2sbus_playback_prepare,
        .trigger =      i2sbus_playback_trigger,
@@ -796,6 +828,7 @@ static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream
 static const struct snd_pcm_ops i2sbus_record_ops = {
        .open =         i2sbus_record_open,
        .close =        i2sbus_record_close,
+       .hw_params =    i2sbus_record_hw_params,
        .hw_free =      i2sbus_record_hw_free,
        .prepare =      i2sbus_record_prepare,
        .trigger =      i2sbus_record_trigger,