ALSA: aoa: i2sbus: clear stale prepared state
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: f3d9478b2c ("[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>
master
parent
d1888bf848
commit
5ed060d549
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue