struct snd_pcm_group {         /* keep linked substreams */
        spinlock_t lock;
+       struct mutex mutex;
        struct list_head substreams;
        int count;
 };
        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
  */
 
 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,
 
 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) \
 
 DEFINE_RWLOCK(snd_pcm_link_rwlock);
 EXPORT_SYMBOL(snd_pcm_link_rwlock);
 
-static DECLARE_RWSEM(snd_pcm_link_rwsem);
+DECLARE_RWSEM(snd_pcm_link_rwsem);
+EXPORT_SYMBOL(snd_pcm_link_rwsem);
 
 static inline mm_segment_t snd_enter_user(void)
 {
        int res = 0;
 
        snd_pcm_group_for_each_entry(s, substream) {
-               if (do_lock && s != substream)
-                       spin_lock_nested(&s->self_group.lock,
-                                        SINGLE_DEPTH_NESTING);
+               if (do_lock && s != substream) {
+                       if (s->pcm->nonatomic)
+                               mutex_lock_nested(&s->self_group.mutex,
+                                                 SINGLE_DEPTH_NESTING);
+                       else
+                               spin_lock_nested(&s->self_group.lock,
+                                                SINGLE_DEPTH_NESTING);
+               }
                res = ops->pre_action(s, state);
                if (res < 0)
                        goto _unlock;
        if (do_lock) {
                /* unlock streams */
                snd_pcm_group_for_each_entry(s1, substream) {
-                       if (s1 != substream)
-                               spin_unlock(&s1->self_group.lock);
+                       if (s1 != substream) {
+                               if (s->pcm->nonatomic)
+                                       mutex_unlock(&s1->self_group.mutex);
+                               else
+                                       spin_unlock(&s1->self_group.lock);
+                       }
                        if (s1 == s)    /* end */
                                break;
                }
        return res;
 }
 
+/* call in mutex-protected context */
+static int snd_pcm_action_mutex(struct action_ops *ops,
+                               struct snd_pcm_substream *substream,
+                               int state)
+{
+       int res;
+
+       if (snd_pcm_stream_linked(substream)) {
+               if (!mutex_trylock(&substream->group->mutex)) {
+                       mutex_unlock(&substream->self_group.mutex);
+                       mutex_lock(&substream->group->mutex);
+                       mutex_lock(&substream->self_group.mutex);
+               }
+               res = snd_pcm_action_group(ops, substream, state, 1);
+               mutex_unlock(&substream->group->mutex);
+       } else {
+               res = snd_pcm_action_single(ops, substream, state);
+       }
+       return res;
+}
+
 /*
  *  Note: call with stream lock
  */
 {
        int res;
 
+       if (substream->pcm->nonatomic)
+               return snd_pcm_action_mutex(ops, substream, state);
+
        if (snd_pcm_stream_linked(substream)) {
                if (!spin_trylock(&substream->group->lock)) {
                        spin_unlock(&substream->self_group.lock);
        return res;
 }
 
+static int snd_pcm_action_lock_mutex(struct action_ops *ops,
+                                    struct snd_pcm_substream *substream,
+                                    int state)
+{
+       int res;
+
+       down_read(&snd_pcm_link_rwsem);
+       if (snd_pcm_stream_linked(substream)) {
+               mutex_lock(&substream->group->mutex);
+               mutex_lock_nested(&substream->self_group.mutex,
+                                 SINGLE_DEPTH_NESTING);
+               res = snd_pcm_action_group(ops, substream, state, 1);
+               mutex_unlock(&substream->self_group.mutex);
+               mutex_unlock(&substream->group->mutex);
+       } else {
+               mutex_lock(&substream->self_group.mutex);
+               res = snd_pcm_action_single(ops, substream, state);
+               mutex_unlock(&substream->self_group.mutex);
+       }
+       up_read(&snd_pcm_link_rwsem);
+       return res;
+}
+
 /*
  *  Note: don't use any locks before
  */
 {
        int res;
 
+       if (substream->pcm->nonatomic)
+               return snd_pcm_action_lock_mutex(ops, substream, state);
+
        read_lock_irq(&snd_pcm_link_rwlock);
        if (snd_pcm_stream_linked(substream)) {
                spin_lock(&substream->group->lock);
        down_write(&snd_pcm_link_rwsem);
        write_lock_irq(&snd_pcm_link_rwlock);
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
-           substream->runtime->status->state != substream1->runtime->status->state) {
+           substream->runtime->status->state != substream1->runtime->status->state ||
+           substream->pcm->nonatomic != substream1->pcm->nonatomic) {
                res = -EBADFD;
                goto _end;
        }
                substream->group = group;
                group = NULL;
                spin_lock_init(&substream->group->lock);
+               mutex_init(&substream->group->mutex);
                INIT_LIST_HEAD(&substream->group->substreams);
                list_add_tail(&substream->link_list, &substream->group->substreams);
                substream->group->count = 1;