summaryrefslogtreecommitdiff
path: root/include/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2014-08-29 17:32:29 +0400
committerTakashi Iwai <tiwai@suse.de>2014-09-03 16:04:08 +0400
commit257f8cce5d40b811d229ed71602882baa0012808 (patch)
tree190a239257c625539a1b157acb3bd0075cdb61dc /include/sound
parent52addcf9d6669fa439387610bc65c92fa0980cef (diff)
downloadlinux-257f8cce5d40b811d229ed71602882baa0012808.tar.xz
ALSA: pcm: Allow nonatomic trigger operations
Currently, many PCM operations are performed in a critical section protected by spinlock, typically the trigger and pointer callbacks are assumed to be atomic. This is basically because some trigger action (e.g. PCM stop after drain or xrun) is done in the interrupt handler. If a driver runs in a threaded irq, however, this doesn't have to be atomic. And many devices want to handle trigger in a non-atomic context due to lengthy communications. This patch tries all PCM calls operational in non-atomic context. What it does is very simple: replaces the substream spinlock with the corresponding substream mutex when pcm->nonatomic flag is set. The driver that wants to use the non-atomic PCM ops just needs to set the flag and keep the rest as is. (Of course, it must not handle any PCM ops in irq context.) Note that the code doesn't check whether it's atomic-safe or not, but trust in 100% that the driver sets pcm->nonatomic correctly. One possible problem is the case where linked PCM substreams have inconsistent nonatomic states. For avoiding this, snd_pcm_link() returns an error if one tries to link an inconsistent PCM substream. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'include/sound')
-rw-r--r--include/sound/pcm.h58
1 files changed, 46 insertions, 12 deletions
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 6f3e10ca0e32..bc79962f4aa6 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -365,6 +365,7 @@ struct snd_pcm_runtime {
struct snd_pcm_group { /* keep linked substreams */
spinlock_t lock;
+ struct mutex mutex;
struct list_head substreams;
int count;
};
@@ -460,6 +461,7 @@ struct snd_pcm {
void (*private_free) (struct snd_pcm *pcm);
struct device *dev; /* actual hw device this belongs to */
bool internal; /* pcm is for internal use only */
+ bool nonatomic; /* whole PCM operations are in non-atomic context */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
struct snd_pcm_oss oss;
#endif
@@ -493,6 +495,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree);
*/
extern rwlock_t snd_pcm_link_rwlock;
+extern struct rw_semaphore snd_pcm_link_rwsem;
int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info);
int snd_pcm_info_user(struct snd_pcm_substream *substream,
@@ -539,38 +542,69 @@ static inline int snd_pcm_stream_linked(struct snd_pcm_substream *substream)
static inline void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
{
- read_lock(&snd_pcm_link_rwlock);
- spin_lock(&substream->self_group.lock);
+ if (substream->pcm->nonatomic) {
+ down_read(&snd_pcm_link_rwsem);
+ mutex_lock(&substream->self_group.mutex);
+ } else {
+ read_lock(&snd_pcm_link_rwlock);
+ spin_lock(&substream->self_group.lock);
+ }
}
static inline void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
{
- spin_unlock(&substream->self_group.lock);
- read_unlock(&snd_pcm_link_rwlock);
+ if (substream->pcm->nonatomic) {
+ mutex_unlock(&substream->self_group.mutex);
+ up_read(&snd_pcm_link_rwsem);
+ } else {
+ spin_unlock(&substream->self_group.lock);
+ read_unlock(&snd_pcm_link_rwlock);
+ }
}
static inline void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
{
- read_lock_irq(&snd_pcm_link_rwlock);
- spin_lock(&substream->self_group.lock);
+ if (substream->pcm->nonatomic) {
+ down_read(&snd_pcm_link_rwsem);
+ mutex_lock(&substream->self_group.mutex);
+ } else {
+ read_lock_irq(&snd_pcm_link_rwlock);
+ spin_lock(&substream->self_group.lock);
+ }
}
static inline void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
{
- spin_unlock(&substream->self_group.lock);
- read_unlock_irq(&snd_pcm_link_rwlock);
+ if (substream->pcm->nonatomic) {
+ mutex_unlock(&substream->self_group.mutex);
+ up_read(&snd_pcm_link_rwsem);
+ } else {
+ spin_unlock(&substream->self_group.lock);
+ read_unlock_irq(&snd_pcm_link_rwlock);
+ }
}
#define snd_pcm_stream_lock_irqsave(substream, flags) \
do { \
- read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \
- spin_lock(&substream->self_group.lock); \
+ if ((substream)->pcm->nonatomic) { \
+ (flags) = 0; /* XXX for avoid warning */ \
+ down_read(&snd_pcm_link_rwsem); \
+ mutex_lock(&(substream)->self_group.mutex); \
+ } else { \
+ read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \
+ spin_lock(&(substream)->self_group.lock); \
+ } \
} while (0)
#define snd_pcm_stream_unlock_irqrestore(substream, flags) \
do { \
- spin_unlock(&substream->self_group.lock); \
- read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \
+ if ((substream)->pcm->nonatomic) { \
+ mutex_unlock(&(substream)->self_group.mutex); \
+ up_read(&snd_pcm_link_rwsem); \
+ } else { \
+ spin_unlock(&(substream)->self_group.lock); \
+ read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \
+ } \
} while (0)
#define snd_pcm_group_for_each_entry(s, substream) \