[PATCH v2 0/6] ALSA: Prep work for PCI rescan support
Hi,
this is a revised patch set as a prep work for the future-planned PCI rescan support(*). The essential part is the patch to track in-flight tasks, and the rest are cleanups and fixes in the core code. With those changes, the driver can perform a sort of software suspend on the device without the hardware intervention, which is required during the PCI BAR movement, for example.
v1->v2: * Move / drop unnecessary snd_power_wait() calls * Rephrase changelogs and comments, reorder patches
Takashi
(*) https://lore.kernel.org/alsa-devel/e25017c6-e5e4-7a24-e793-14a2e70a434e@amd....
===
Takashi Iwai (6): ALSA: core: Use READ_ONCE() / WRITE_ONCE() for power state change ALSA: control: Track in-flight control read/write/tlv accesses ALSA: control: Drop superfluous snd_power_wait() calls ALSA: control: Minor optimization for SNDRV_CTL_IOCTL_POWER_STATE ALSA: Drop superfluous argument from snd_power_wait() ALSA: pcm: Block the release until the system resume finishes
include/sound/core.h | 50 ++++++++++++++++++++++++++++++++++--- sound/core/control.c | 38 +++++++++++++--------------- sound/core/control_compat.c | 14 +++-------- sound/core/init.c | 46 ++++++++++++++++++++++++++++------ sound/core/pcm_native.c | 6 ++++- sound/soc/soc-core.c | 2 +- sound/usb/usx2y/us122l.c | 2 +- 7 files changed, 113 insertions(+), 45 deletions(-)
We need proper barriers to handle the power state change of the card from different CPUs.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/core.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/sound/core.h b/include/sound/core.h index 1f9aef0adbc9..d69dfc6c4953 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -142,12 +142,12 @@ struct snd_card { #ifdef CONFIG_PM static inline unsigned int snd_power_get_state(struct snd_card *card) { - return card->power_state; + return READ_ONCE(card->power_state); }
static inline void snd_power_change_state(struct snd_card *card, unsigned int state) { - card->power_state = state; + WRITE_ONCE(card->power_state, state); wake_up(&card->power_sleep); }
Although the power state check is performed in various places (e.g. at the entrance of quite a few ioctls), there can be still some pending tasks that already went into the ioctl handler or other ops, and those may access the hardware even after the power state check. For example, kcontrol access ioctl paths that call info/get/put callbacks may update the hardware registers. If a system wants to assure the free from such hw access (like the case of PCI rescan feature we're going to implement in future), this situation must be avoided, and we have to sync such in-flight tasks finishing beforehand.
For that purpose, this patch introduces a few new things in core code: - A refcount, power_ref, and a wait queue, power_ref_sleep, to the card object - A few new helpers, snd_power_ref(), snd_power_unref(), snd_power_ref_and_wait(), and snd_power_sync_ref()
In the code paths that call kctl info/read/write/tlv ops, we check the power state with the newly introduced snd_power_ref_and_wait(). This function also takes the card.power_ref refcount for tracking this in-flight task. Once after the access finishes, snd_power_unref() is called to released the refcount in return. So the driver can sync via snd_power_sync_ref() assuring that all in-flight tasks have been finished.
As of this patch, snd_power_sync_ref() is called only at snd_card_disconnect(), but it'll be used in other places in future.
Note that atomic_t is used for power_ref intentionally instead of refcount_t. It's because of the design of refcount_t type; refcount_t cannot be zero-based, and it cannot do dec_and_test() call for multiple times, hence it's not suitable for our purpose.
Also, this patch changes snd_power_wait() to accept only SNDRV_CTL_POWER_D0, which is the only value that makes sense. In later patch, the snd_power_wait() calls will be cleaned up.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/core.h | 42 +++++++++++++++++++++++++++++++ sound/core/control.c | 23 +++++++++++++---- sound/core/control_compat.c | 5 +++- sound/core/init.c | 50 +++++++++++++++++++++++++++++++------ 4 files changed, 106 insertions(+), 14 deletions(-)
diff --git a/include/sound/core.h b/include/sound/core.h index d69dfc6c4953..3acb754108b1 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -128,7 +128,9 @@ struct snd_card {
#ifdef CONFIG_PM unsigned int power_state; /* power state */ + atomic_t power_ref; wait_queue_head_t power_sleep; + wait_queue_head_t power_ref_sleep; #endif
#if IS_ENABLED(CONFIG_SND_MIXER_OSS) @@ -151,12 +153,52 @@ static inline void snd_power_change_state(struct snd_card *card, unsigned int st wake_up(&card->power_sleep); }
+/** + * snd_power_ref - Take the reference count for power control + * @card: sound card object + * + * The power_ref reference of the card is used for managing to block + * the snd_power_sync_ref() operation. This function increments the reference. + * The counterpart snd_power_unref() has to be called appropriately later. + */ +static inline void snd_power_ref(struct snd_card *card) +{ + atomic_inc(&card->power_ref); +} + +/** + * snd_power_unref - Release the reference count for power control + * @card: sound card object + */ +static inline void snd_power_unref(struct snd_card *card) +{ + if (atomic_dec_and_test(&card->power_ref)) + wake_up(&card->power_ref_sleep); +} + +/** + * snd_power_sync_ref - wait until the card power_ref is freed + * @card: sound card object + * + * This function is used to synchronize with the pending power_ref being + * released. + */ +static inline void snd_power_sync_ref(struct snd_card *card) +{ + wait_event(card->power_ref_sleep, !atomic_read(&card->power_ref)); +} + /* init.c */ int snd_power_wait(struct snd_card *card, unsigned int power_state); +int snd_power_ref_and_wait(struct snd_card *card);
#else /* ! CONFIG_PM */
static inline int snd_power_wait(struct snd_card *card, unsigned int state) { return 0; } +static inline void snd_power_ref(struct snd_card *card) {} +static inline void snd_power_unref(struct snd_card *card) {} +static inline int snd_power_ref_and_wait(struct snd_card *card) { return 0; } +static inline void snd_power_sync_ref(struct snd_card *card) {} #define snd_power_get_state(card) ({ (void)(card); SNDRV_CTL_POWER_D0; }) #define snd_power_change_state(card, state) do { (void)(card); } while (0)
diff --git a/sound/core/control.c b/sound/core/control.c index 498e3701514a..638da34605ba 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -995,7 +995,10 @@ static int __snd_ctl_elem_info(struct snd_card *card, #ifdef CONFIG_SND_DEBUG info->access = 0; #endif - result = kctl->info(kctl, info); + result = snd_power_ref_and_wait(card); + if (!result) + result = kctl->info(kctl, info); + snd_power_unref(card); if (result >= 0) { snd_BUG_ON(info->access); index_offset = snd_ctl_get_ioff(kctl, &info->id); @@ -1088,7 +1091,10 @@ static int snd_ctl_elem_read(struct snd_card *card,
if (!snd_ctl_skip_validation(&info)) fill_remaining_elem_value(control, &info, pattern); - ret = kctl->get(kctl, control); + ret = snd_power_ref_and_wait(card); + if (!ret) + ret = kctl->get(kctl, control); + snd_power_unref(card); if (ret < 0) return ret; if (!snd_ctl_skip_validation(&info) && @@ -1154,7 +1160,10 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, }
snd_ctl_build_ioff(&control->id, kctl, index_offset); - result = kctl->put(kctl, control); + result = snd_power_ref_and_wait(card); + if (!result) + result = kctl->put(kctl, control); + snd_power_unref(card); if (result < 0) { up_write(&card->controls_rwsem); return result; @@ -1669,7 +1678,7 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag, {SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND}, }; struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)]; - int i; + int i, ret;
/* Check support of the request for this element. */ for (i = 0; i < ARRAY_SIZE(pairs); ++i) { @@ -1687,7 +1696,11 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag, vd->owner != NULL && vd->owner != file) return -EPERM;
- return kctl->tlv.c(kctl, op_flag, size, buf); + ret = snd_power_ref_and_wait(file->card); + if (!ret) + ret = kctl->tlv.c(kctl, op_flag, size, buf); + snd_power_unref(file->card); + return ret; }
static int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id, diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 1d708aab9c98..19133ee076c5 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -187,7 +187,10 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, return -ENOMEM; } info->id = *id; - err = kctl->info(kctl, info); + err = snd_power_ref_and_wait(card); + if (!err) + err = kctl->info(kctl, info); + snd_power_unref(card); up_read(&card->controls_rwsem); if (err >= 0) { err = info->type; diff --git a/sound/core/init.c b/sound/core/init.c index ef41f5b3a240..2c62e035bc62 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -220,6 +220,8 @@ int snd_card_new(struct device *parent, int idx, const char *xid, mutex_init(&card->memory_mutex); #ifdef CONFIG_PM init_waitqueue_head(&card->power_sleep); + init_waitqueue_head(&card->power_ref_sleep); + atomic_set(&card->power_ref, 0); #endif init_waitqueue_head(&card->remove_sleep); card->sync_irq = -1; @@ -442,6 +444,7 @@ int snd_card_disconnect(struct snd_card *card)
#ifdef CONFIG_PM wake_up(&card->power_sleep); + snd_power_sync_ref(card); #endif return 0; } @@ -1002,21 +1005,28 @@ EXPORT_SYMBOL(snd_card_file_remove);
#ifdef CONFIG_PM /** - * snd_power_wait - wait until the power-state is changed. - * @card: soundcard structure - * @power_state: expected power state + * snd_power_ref_and_wait - wait until the card gets powered up + * @card: soundcard structure * - * Waits until the power-state is changed. + * Take the power_ref reference count of the given card, and + * wait until the card gets powered up to SNDRV_CTL_POWER_D0 state. + * The refcount is down again while sleeping until power-up, hence this + * function can be used for syncing the floating control ops accesses, + * typically around calling control ops. * - * Return: Zero if successful, or a negative error code. + * The caller needs to pull down the refcount via snd_power_unref() later + * no matter whether the error is returned from this function or not. + * + * Return: Zero if successful, or a negative error code. */ -int snd_power_wait(struct snd_card *card, unsigned int power_state) +int snd_power_ref_and_wait(struct snd_card *card) { wait_queue_entry_t wait; int result = 0;
+ snd_power_ref(card); /* fastpath */ - if (snd_power_get_state(card) == power_state) + if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) return 0; init_waitqueue_entry(&wait, current); add_wait_queue(&card->power_sleep, &wait); @@ -1025,13 +1035,37 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state) result = -ENODEV; break; } - if (snd_power_get_state(card) == power_state) + if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) break; + snd_power_unref(card); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(30 * HZ); + snd_power_ref(card); } remove_wait_queue(&card->power_sleep, &wait); return result; } +EXPORT_SYMBOL_GPL(snd_power_ref_and_wait); + +/** + * snd_power_wait - wait until the card gets powered up (old form) + * @card: soundcard structure + * @power_state: expected power state + * + * Wait until the card gets powered up to SNDRV_CTL_POWER_D0 state. + * @power_state must be SNDRV_CTL_POWER_D0. + * + * Return: Zero if successful, or a negative error code. + */ +int snd_power_wait(struct snd_card *card, unsigned int power_state) +{ + int ret; + + if (WARN_ON(power_state != SNDRV_CTL_POWER_D0)) + return 0; + ret = snd_power_ref_and_wait(card); + snd_power_unref(card); + return ret; +} EXPORT_SYMBOL(snd_power_wait); #endif /* CONFIG_PM */
Now we have more fine-grained power controls in each kcontrol ops, the coarse checks of snd_power_wait() in a few control ioctls became superfluous. Let's drop them.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/control.c | 11 ----------- sound/core/control_compat.c | 9 --------- 2 files changed, 20 deletions(-)
diff --git a/sound/core/control.c b/sound/core/control.c index 638da34605ba..7fe901384c9c 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1045,9 +1045,6 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT; - result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0); - if (result < 0) - return result; result = snd_ctl_elem_info(ctl, &info); if (result < 0) return result; @@ -1119,10 +1116,6 @@ static int snd_ctl_elem_read_user(struct snd_card *card, if (IS_ERR(control)) return PTR_ERR(control);
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (result < 0) - goto error; - down_read(&card->controls_rwsem); result = snd_ctl_elem_read(card, control); up_read(&card->controls_rwsem); @@ -1192,10 +1185,6 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file, return PTR_ERR(control);
card = file->card; - result = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (result < 0) - goto error; - result = snd_ctl_elem_write(card, file, control); if (result < 0) goto error; diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 19133ee076c5..470dabc60aa0 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -96,9 +96,6 @@ static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl, if (get_user(data->value.enumerated.item, &data32->value.enumerated.item)) goto error;
- err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0); - if (err < 0) - goto error; err = snd_ctl_elem_info(ctl, data); if (err < 0) goto error; @@ -301,9 +298,6 @@ static int ctl_elem_read_user(struct snd_card *card, if (err < 0) goto error;
- err = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (err < 0) - goto error; err = snd_ctl_elem_read(card, data); if (err < 0) goto error; @@ -329,9 +323,6 @@ static int ctl_elem_write_user(struct snd_ctl_file *file, if (err < 0) goto error;
- err = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (err < 0) - goto error; err = snd_ctl_elem_write(card, file, data); if (err < 0) goto error;
Long long time ago, before the proper PM framework was introduced, it was still possible to reach SNDRV_CTL_IOCTL_POWER ioctl during the power off state. This ioctl existed as a main control for the suspend resume state in the past, but the feature was already dropped along with the standard PM framework. Now the read part, SNDRV_IOCTL_POWER_STATE ioctl, returns practically always D0, and we can do some minor optimization there.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/control.c | 4 ---- 1 file changed, 4 deletions(-)
diff --git a/sound/core/control.c b/sound/core/control.c index 7fe901384c9c..a25c0d64d104 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1817,11 +1817,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg case SNDRV_CTL_IOCTL_POWER: return -ENOPROTOOPT; case SNDRV_CTL_IOCTL_POWER_STATE: -#ifdef CONFIG_PM - return put_user(card->power_state, ip) ? -EFAULT : 0; -#else return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0; -#endif } down_read(&snd_ioctl_rwsem); list_for_each_entry(p, &snd_control_ioctls, list) {
The power_state argument of snd_power_wait() is superfluous, receiving only SNDRV_POWER_STATE_D0. Let's drop it in all callers for simplicity.
Cc: Mark Brown broonie@kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/core.h | 4 ++-- sound/core/init.c | 6 +----- sound/core/pcm_native.c | 2 +- sound/soc/soc-core.c | 2 +- sound/usb/usx2y/us122l.c | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/include/sound/core.h b/include/sound/core.h index 3acb754108b1..c4ade121727d 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -189,12 +189,12 @@ static inline void snd_power_sync_ref(struct snd_card *card) }
/* init.c */ -int snd_power_wait(struct snd_card *card, unsigned int power_state); +int snd_power_wait(struct snd_card *card); int snd_power_ref_and_wait(struct snd_card *card);
#else /* ! CONFIG_PM */
-static inline int snd_power_wait(struct snd_card *card, unsigned int state) { return 0; } +static inline int snd_power_wait(struct snd_card *card) { return 0; } static inline void snd_power_ref(struct snd_card *card) {} static inline void snd_power_unref(struct snd_card *card) {} static inline int snd_power_ref_and_wait(struct snd_card *card) { return 0; } diff --git a/sound/core/init.c b/sound/core/init.c index 2c62e035bc62..70114fd26956 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -1050,19 +1050,15 @@ EXPORT_SYMBOL_GPL(snd_power_ref_and_wait); /** * snd_power_wait - wait until the card gets powered up (old form) * @card: soundcard structure - * @power_state: expected power state * * Wait until the card gets powered up to SNDRV_CTL_POWER_D0 state. - * @power_state must be SNDRV_CTL_POWER_D0. * * Return: Zero if successful, or a negative error code. */ -int snd_power_wait(struct snd_card *card, unsigned int power_state) +int snd_power_wait(struct snd_card *card) { int ret;
- if (WARN_ON(power_state != SNDRV_CTL_POWER_D0)) - return 0; ret = snd_power_ref_and_wait(card); snd_power_unref(card); return ret; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 8dbe86cf2e4f..82f80d0c068b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3193,7 +3193,7 @@ static int snd_pcm_common_ioctl(struct file *file, if (PCM_RUNTIME_CHECK(substream)) return -ENXIO;
- res = snd_power_wait(substream->pcm->card, SNDRV_CTL_POWER_D0); + res = snd_power_wait(substream->pcm->card); if (res < 0) return res;
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1c0904acb935..ddc65c12f6f4 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -580,7 +580,7 @@ int snd_soc_suspend(struct device *dev) * Due to the resume being scheduled into a workqueue we could * suspend before that's finished - wait for it to complete. */ - snd_power_wait(card->snd_card, SNDRV_CTL_POWER_D0); + snd_power_wait(card->snd_card);
/* we're going to block userspace touching us until resume completes */ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index 6e1bfe894dd5..596a1a1fea49 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -379,7 +379,7 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, if (cfg.period_frames < min_period_frames) return -EINVAL;
- snd_power_wait(hw->card, SNDRV_CTL_POWER_D0); + snd_power_wait(hw->card);
mutex_lock(&us122l->mutex); s = us122l->sk.s;
On Sun, May 23, 2021 at 11:09:19AM +0200, Takashi Iwai wrote:
The power_state argument of snd_power_wait() is superfluous, receiving only SNDRV_POWER_STATE_D0. Let's drop it in all callers for simplicity.
Acked-by: Mark Brown broonie@kernel.org
The normal PCM operations are already blocked during the card power off state in the PCM common ioctl handler, but the release isn't covered. As the PCM stream release may also access the hardware, let's block the release until the card power turns on.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/pcm_native.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 82f80d0c068b..11acea02bc74 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2799,6 +2799,10 @@ static int snd_pcm_release(struct inode *inode, struct file *file) if (snd_BUG_ON(!substream)) return -ENXIO; pcm = substream->pcm; + + /* block until the device gets woken up as it may touch the hardware */ + snd_power_wait(pcm->card); + mutex_lock(&pcm->open_mutex); snd_pcm_release_substream(substream); kfree(pcm_file);
Dne 23. 05. 21 v 11:09 Takashi Iwai napsal(a):
Hi,
this is a revised patch set as a prep work for the future-planned PCI rescan support(*). The essential part is the patch to track in-flight tasks, and the rest are cleanups and fixes in the core code. With those changes, the driver can perform a sort of software suspend on the device without the hardware intervention, which is required during the PCI BAR movement, for example.
v1->v2:
- Move / drop unnecessary snd_power_wait() calls
- Rephrase changelogs and comments, reorder patches
Reviewed-by: Jaroslav Kysela perex@perex.cz
Takashi
(*) https://lore.kernel.org/alsa-devel/e25017c6-e5e4-7a24-e793-14a2e70a434e@amd....
===
Takashi Iwai (6): ALSA: core: Use READ_ONCE() / WRITE_ONCE() for power state change ALSA: control: Track in-flight control read/write/tlv accesses ALSA: control: Drop superfluous snd_power_wait() calls ALSA: control: Minor optimization for SNDRV_CTL_IOCTL_POWER_STATE ALSA: Drop superfluous argument from snd_power_wait() ALSA: pcm: Block the release until the system resume finishes
include/sound/core.h | 50 ++++++++++++++++++++++++++++++++++--- sound/core/control.c | 38 +++++++++++++--------------- sound/core/control_compat.c | 14 +++-------- sound/core/init.c | 46 ++++++++++++++++++++++++++++------ sound/core/pcm_native.c | 6 ++++- sound/soc/soc-core.c | 2 +- sound/usb/usx2y/us122l.c | 2 +- 7 files changed, 113 insertions(+), 45 deletions(-)
On Sun, 23 May 2021 11:09:14 +0200, Takashi Iwai wrote:
Hi,
this is a revised patch set as a prep work for the future-planned PCI rescan support(*). The essential part is the patch to track in-flight tasks, and the rest are cleanups and fixes in the core code. With those changes, the driver can perform a sort of software suspend on the device without the hardware intervention, which is required during the PCI BAR movement, for example.
v1->v2:
- Move / drop unnecessary snd_power_wait() calls
- Rephrase changelogs and comments, reorder patches
Takashi
(*) https://lore.kernel.org/alsa-devel/e25017c6-e5e4-7a24-e793-14a2e70a434e@amd....
===
Takashi Iwai (6): ALSA: core: Use READ_ONCE() / WRITE_ONCE() for power state change ALSA: control: Track in-flight control read/write/tlv accesses ALSA: control: Drop superfluous snd_power_wait() calls ALSA: control: Minor optimization for SNDRV_CTL_IOCTL_POWER_STATE ALSA: Drop superfluous argument from snd_power_wait() ALSA: pcm: Block the release until the system resume finishes
Now all patches merged to topic/pci-rescan-v2 branch, and into for-next branch. The former is a branch freshly based on 5.13-rc1, so that other trees may pull cleanly.
Takashi
participants (3)
-
Jaroslav Kysela
-
Mark Brown
-
Takashi Iwai