Commit f20fdd43 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/pci-rescan-prep-v2' into for-next

Pull PCI rescan prep work.

Link: https://lore.kernel.org/r/20210523090920.15345-1-tiwai@suse.de
parents 13d11f14 534a427b
...@@ -128,7 +128,9 @@ struct snd_card { ...@@ -128,7 +128,9 @@ struct snd_card {
#ifdef CONFIG_PM #ifdef CONFIG_PM
unsigned int power_state; /* power state */ unsigned int power_state; /* power state */
atomic_t power_ref;
wait_queue_head_t power_sleep; wait_queue_head_t power_sleep;
wait_queue_head_t power_ref_sleep;
#endif #endif
#if IS_ENABLED(CONFIG_SND_MIXER_OSS) #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
...@@ -142,21 +144,61 @@ struct snd_card { ...@@ -142,21 +144,61 @@ struct snd_card {
#ifdef CONFIG_PM #ifdef CONFIG_PM
static inline unsigned int snd_power_get_state(struct snd_card *card) 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) 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); 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 */ /* 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 */ #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; }
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_get_state(card) ({ (void)(card); SNDRV_CTL_POWER_D0; })
#define snd_power_change_state(card, state) do { (void)(card); } while (0) #define snd_power_change_state(card, state) do { (void)(card); } while (0)
......
...@@ -995,7 +995,10 @@ static int __snd_ctl_elem_info(struct snd_card *card, ...@@ -995,7 +995,10 @@ static int __snd_ctl_elem_info(struct snd_card *card,
#ifdef CONFIG_SND_DEBUG #ifdef CONFIG_SND_DEBUG
info->access = 0; info->access = 0;
#endif #endif
result = snd_power_ref_and_wait(card);
if (!result)
result = kctl->info(kctl, info); result = kctl->info(kctl, info);
snd_power_unref(card);
if (result >= 0) { if (result >= 0) {
snd_BUG_ON(info->access); snd_BUG_ON(info->access);
index_offset = snd_ctl_get_ioff(kctl, &info->id); index_offset = snd_ctl_get_ioff(kctl, &info->id);
...@@ -1042,9 +1045,6 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl, ...@@ -1042,9 +1045,6 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
if (copy_from_user(&info, _info, sizeof(info))) if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT; return -EFAULT;
result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
if (result < 0)
return result;
result = snd_ctl_elem_info(ctl, &info); result = snd_ctl_elem_info(ctl, &info);
if (result < 0) if (result < 0)
return result; return result;
...@@ -1088,7 +1088,10 @@ static int snd_ctl_elem_read(struct snd_card *card, ...@@ -1088,7 +1088,10 @@ static int snd_ctl_elem_read(struct snd_card *card,
if (!snd_ctl_skip_validation(&info)) if (!snd_ctl_skip_validation(&info))
fill_remaining_elem_value(control, &info, pattern); fill_remaining_elem_value(control, &info, pattern);
ret = snd_power_ref_and_wait(card);
if (!ret)
ret = kctl->get(kctl, control); ret = kctl->get(kctl, control);
snd_power_unref(card);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (!snd_ctl_skip_validation(&info) && if (!snd_ctl_skip_validation(&info) &&
...@@ -1113,10 +1116,6 @@ static int snd_ctl_elem_read_user(struct snd_card *card, ...@@ -1113,10 +1116,6 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
if (IS_ERR(control)) if (IS_ERR(control))
return PTR_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); down_read(&card->controls_rwsem);
result = snd_ctl_elem_read(card, control); result = snd_ctl_elem_read(card, control);
up_read(&card->controls_rwsem); up_read(&card->controls_rwsem);
...@@ -1154,7 +1153,10 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, ...@@ -1154,7 +1153,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); snd_ctl_build_ioff(&control->id, kctl, index_offset);
result = snd_power_ref_and_wait(card);
if (!result)
result = kctl->put(kctl, control); result = kctl->put(kctl, control);
snd_power_unref(card);
if (result < 0) { if (result < 0) {
up_write(&card->controls_rwsem); up_write(&card->controls_rwsem);
return result; return result;
...@@ -1183,10 +1185,6 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file, ...@@ -1183,10 +1185,6 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
return PTR_ERR(control); return PTR_ERR(control);
card = file->card; 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); result = snd_ctl_elem_write(card, file, control);
if (result < 0) if (result < 0)
goto error; goto error;
...@@ -1669,7 +1667,7 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag, ...@@ -1669,7 +1667,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}, {SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
}; };
struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)]; 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. */ /* Check support of the request for this element. */
for (i = 0; i < ARRAY_SIZE(pairs); ++i) { for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
...@@ -1687,7 +1685,11 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag, ...@@ -1687,7 +1685,11 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
vd->owner != NULL && vd->owner != file) vd->owner != NULL && vd->owner != file)
return -EPERM; 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, static int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id,
...@@ -1815,11 +1817,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg ...@@ -1815,11 +1817,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_POWER: case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT; return -ENOPROTOOPT;
case SNDRV_CTL_IOCTL_POWER_STATE: 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; return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
#endif
} }
down_read(&snd_ioctl_rwsem); down_read(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_ioctls, list) { list_for_each_entry(p, &snd_control_ioctls, list) {
......
...@@ -96,9 +96,6 @@ static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl, ...@@ -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)) if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
goto error; goto error;
err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
if (err < 0)
goto error;
err = snd_ctl_elem_info(ctl, data); err = snd_ctl_elem_info(ctl, data);
if (err < 0) if (err < 0)
goto error; goto error;
...@@ -187,7 +184,10 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, ...@@ -187,7 +184,10 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
return -ENOMEM; return -ENOMEM;
} }
info->id = *id; info->id = *id;
err = snd_power_ref_and_wait(card);
if (!err)
err = kctl->info(kctl, info); err = kctl->info(kctl, info);
snd_power_unref(card);
up_read(&card->controls_rwsem); up_read(&card->controls_rwsem);
if (err >= 0) { if (err >= 0) {
err = info->type; err = info->type;
...@@ -298,9 +298,6 @@ static int ctl_elem_read_user(struct snd_card *card, ...@@ -298,9 +298,6 @@ static int ctl_elem_read_user(struct snd_card *card,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (err < 0)
goto error;
err = snd_ctl_elem_read(card, data); err = snd_ctl_elem_read(card, data);
if (err < 0) if (err < 0)
goto error; goto error;
...@@ -326,9 +323,6 @@ static int ctl_elem_write_user(struct snd_ctl_file *file, ...@@ -326,9 +323,6 @@ static int ctl_elem_write_user(struct snd_ctl_file *file,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (err < 0)
goto error;
err = snd_ctl_elem_write(card, file, data); err = snd_ctl_elem_write(card, file, data);
if (err < 0) if (err < 0)
goto error; goto error;
......
...@@ -220,6 +220,8 @@ int snd_card_new(struct device *parent, int idx, const char *xid, ...@@ -220,6 +220,8 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
mutex_init(&card->memory_mutex); mutex_init(&card->memory_mutex);
#ifdef CONFIG_PM #ifdef CONFIG_PM
init_waitqueue_head(&card->power_sleep); init_waitqueue_head(&card->power_sleep);
init_waitqueue_head(&card->power_ref_sleep);
atomic_set(&card->power_ref, 0);
#endif #endif
init_waitqueue_head(&card->remove_sleep); init_waitqueue_head(&card->remove_sleep);
card->sync_irq = -1; card->sync_irq = -1;
...@@ -442,6 +444,7 @@ int snd_card_disconnect(struct snd_card *card) ...@@ -442,6 +444,7 @@ int snd_card_disconnect(struct snd_card *card)
#ifdef CONFIG_PM #ifdef CONFIG_PM
wake_up(&card->power_sleep); wake_up(&card->power_sleep);
snd_power_sync_ref(card);
#endif #endif
return 0; return 0;
} }
...@@ -1002,21 +1005,28 @@ EXPORT_SYMBOL(snd_card_file_remove); ...@@ -1002,21 +1005,28 @@ EXPORT_SYMBOL(snd_card_file_remove);
#ifdef CONFIG_PM #ifdef CONFIG_PM
/** /**
* snd_power_wait - wait until the power-state is changed. * snd_power_ref_and_wait - wait until the card gets powered up
* @card: soundcard structure * @card: soundcard structure
* @power_state: expected power state
* *
* 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.
*
* 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. * 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; wait_queue_entry_t wait;
int result = 0; int result = 0;
snd_power_ref(card);
/* fastpath */ /* fastpath */
if (snd_power_get_state(card) == power_state) if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0)
return 0; return 0;
init_waitqueue_entry(&wait, current); init_waitqueue_entry(&wait, current);
add_wait_queue(&card->power_sleep, &wait); add_wait_queue(&card->power_sleep, &wait);
...@@ -1025,13 +1035,33 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state) ...@@ -1025,13 +1035,33 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state)
result = -ENODEV; result = -ENODEV;
break; break;
} }
if (snd_power_get_state(card) == power_state) if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0)
break; break;
snd_power_unref(card);
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(30 * HZ); schedule_timeout(30 * HZ);
snd_power_ref(card);
} }
remove_wait_queue(&card->power_sleep, &wait); remove_wait_queue(&card->power_sleep, &wait);
return result; 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
*
* Wait until the card gets powered up to SNDRV_CTL_POWER_D0 state.
*
* Return: Zero if successful, or a negative error code.
*/
int snd_power_wait(struct snd_card *card)
{
int ret;
ret = snd_power_ref_and_wait(card);
snd_power_unref(card);
return ret;
}
EXPORT_SYMBOL(snd_power_wait); EXPORT_SYMBOL(snd_power_wait);
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
...@@ -2799,6 +2799,10 @@ static int snd_pcm_release(struct inode *inode, struct file *file) ...@@ -2799,6 +2799,10 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
if (snd_BUG_ON(!substream)) if (snd_BUG_ON(!substream))
return -ENXIO; return -ENXIO;
pcm = substream->pcm; 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); mutex_lock(&pcm->open_mutex);
snd_pcm_release_substream(substream); snd_pcm_release_substream(substream);
kfree(pcm_file); kfree(pcm_file);
...@@ -3193,7 +3197,7 @@ static int snd_pcm_common_ioctl(struct file *file, ...@@ -3193,7 +3197,7 @@ static int snd_pcm_common_ioctl(struct file *file,
if (PCM_RUNTIME_CHECK(substream)) if (PCM_RUNTIME_CHECK(substream))
return -ENXIO; return -ENXIO;
res = snd_power_wait(substream->pcm->card, SNDRV_CTL_POWER_D0); res = snd_power_wait(substream->pcm->card);
if (res < 0) if (res < 0)
return res; return res;
......
...@@ -580,7 +580,7 @@ int snd_soc_suspend(struct device *dev) ...@@ -580,7 +580,7 @@ int snd_soc_suspend(struct device *dev)
* Due to the resume being scheduled into a workqueue we could * Due to the resume being scheduled into a workqueue we could
* suspend before that's finished - wait for it to complete. * 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 */ /* we're going to block userspace touching us until resume completes */
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot);
......
...@@ -385,7 +385,7 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, ...@@ -385,7 +385,7 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
if (cfg.period_frames < min_period_frames) if (cfg.period_frames < min_period_frames)
return -EINVAL; return -EINVAL;
snd_power_wait(hw->card, SNDRV_CTL_POWER_D0); snd_power_wait(hw->card);
mutex_lock(&us122l->mutex); mutex_lock(&us122l->mutex);
s = us122l->sk.s; s = us122l->sk.s;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment