forked from Minki/linux
Merge branch 'topic/card-disconnect' into for-next
Pull snd_card_disconnect_sync() extension for ASoC hot-unplug support. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
commit
1a01901575
@ -133,6 +133,7 @@ struct snd_card {
|
||||
struct device card_dev; /* cardX object for sysfs */
|
||||
const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
|
||||
bool registered; /* card_dev is registered? */
|
||||
wait_queue_head_t remove_sleep;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
unsigned int power_state; /* power state */
|
||||
@ -240,6 +241,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
|
||||
struct snd_card **card_ret);
|
||||
|
||||
int snd_card_disconnect(struct snd_card *card);
|
||||
void snd_card_disconnect_sync(struct snd_card *card);
|
||||
int snd_card_free(struct snd_card *card);
|
||||
int snd_card_free_when_closed(struct snd_card *card);
|
||||
void snd_card_set_id(struct snd_card *card, const char *id);
|
||||
|
@ -255,6 +255,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
|
||||
#ifdef CONFIG_PM
|
||||
init_waitqueue_head(&card->power_sleep);
|
||||
#endif
|
||||
init_waitqueue_head(&card->remove_sleep);
|
||||
|
||||
device_initialize(&card->card_dev);
|
||||
card->card_dev.parent = parent;
|
||||
@ -452,6 +453,35 @@ int snd_card_disconnect(struct snd_card *card)
|
||||
}
|
||||
EXPORT_SYMBOL(snd_card_disconnect);
|
||||
|
||||
/**
|
||||
* snd_card_disconnect_sync - disconnect card and wait until files get closed
|
||||
* @card: card object to disconnect
|
||||
*
|
||||
* This calls snd_card_disconnect() for disconnecting all belonging components
|
||||
* and waits until all pending files get closed.
|
||||
* It assures that all accesses from user-space finished so that the driver
|
||||
* can release its resources gracefully.
|
||||
*/
|
||||
void snd_card_disconnect_sync(struct snd_card *card)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = snd_card_disconnect(card);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev,
|
||||
"snd_card_disconnect error (%d), skipping sync\n",
|
||||
err);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irq(&card->files_lock);
|
||||
wait_event_lock_irq(card->remove_sleep,
|
||||
list_empty(&card->files_list),
|
||||
card->files_lock);
|
||||
spin_unlock_irq(&card->files_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_card_disconnect_sync);
|
||||
|
||||
static int snd_card_do_free(struct snd_card *card)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
|
||||
@ -957,6 +987,8 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (list_empty(&card->files_list))
|
||||
wake_up_all(&card->remove_sleep);
|
||||
spin_unlock(&card->files_lock);
|
||||
if (!found) {
|
||||
dev_err(card->dev, "card file remove problem (%p)\n", file);
|
||||
|
@ -775,6 +775,9 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
|
||||
.dev_register = snd_pcm_dev_register,
|
||||
.dev_disconnect = snd_pcm_dev_disconnect,
|
||||
};
|
||||
static struct snd_device_ops internal_ops = {
|
||||
.dev_free = snd_pcm_dev_free,
|
||||
};
|
||||
|
||||
if (snd_BUG_ON(!card))
|
||||
return -ENXIO;
|
||||
@ -801,7 +804,8 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
|
||||
if (err < 0)
|
||||
goto free_pcm;
|
||||
|
||||
err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops);
|
||||
err = snd_device_new(card, SNDRV_DEV_PCM, pcm,
|
||||
internal ? &internal_ops : &ops);
|
||||
if (err < 0)
|
||||
goto free_pcm;
|
||||
|
||||
@ -1099,8 +1103,6 @@ static int snd_pcm_dev_register(struct snd_device *device)
|
||||
if (snd_BUG_ON(!device || !device->device_data))
|
||||
return -ENXIO;
|
||||
pcm = device->device_data;
|
||||
if (pcm->internal)
|
||||
return 0;
|
||||
|
||||
mutex_lock(®ister_mutex);
|
||||
err = snd_pcm_add(pcm);
|
||||
@ -1152,6 +1154,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
|
||||
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) {
|
||||
snd_pcm_stream_lock_irq(substream);
|
||||
if (substream->runtime) {
|
||||
if (snd_pcm_running(substream))
|
||||
snd_pcm_stop(substream,
|
||||
SNDRV_PCM_STATE_DISCONNECTED);
|
||||
/* to be sure, set the state unconditionally */
|
||||
substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
|
||||
wake_up(&substream->runtime->sleep);
|
||||
wake_up(&substream->runtime->tsleep);
|
||||
@ -1159,12 +1165,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
}
|
||||
}
|
||||
if (!pcm->internal) {
|
||||
pcm_call_notify(pcm, n_disconnect);
|
||||
}
|
||||
|
||||
pcm_call_notify(pcm, n_disconnect);
|
||||
for (cidx = 0; cidx < 2; cidx++) {
|
||||
if (!pcm->internal)
|
||||
snd_unregister_device(&pcm->streams[cidx].dev);
|
||||
snd_unregister_device(&pcm->streams[cidx].dev);
|
||||
free_chmap(&pcm->streams[cidx]);
|
||||
}
|
||||
mutex_unlock(&pcm->open_mutex);
|
||||
|
Loading…
Reference in New Issue
Block a user