ALSA: pcm: Add the support for sync-stop operation
[sfrench/cifs-2.6.git] / sound / core / pcm_native.c
index 91c6ad58729fe7c8d6856a8f74b5ed547d19b179..163d621ff238e3ffcf6e46a39595be3ccf5610b7 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/pm_qos.h>
 #include <linux/io.h>
 #include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/info.h>
@@ -177,6 +178,16 @@ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
 }
 EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
 
+/* Run PCM ioctl ops */
+static int snd_pcm_ops_ioctl(struct snd_pcm_substream *substream,
+                            unsigned cmd, void *arg)
+{
+       if (substream->ops->ioctl)
+               return substream->ops->ioctl(substream, cmd, arg);
+       else
+               return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
 int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
 {
        struct snd_pcm *pcm = substream->pcm;
@@ -222,7 +233,8 @@ static bool hw_support_mmap(struct snd_pcm_substream *substream)
                return false;
 
        if (substream->ops->mmap ||
-           substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_DEV)
+           (substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_DEV &&
+            substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_DEV_UC))
                return true;
 
        return dma_can_mmap(substream->dma_buffer.dev.dev);
@@ -446,8 +458,9 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
                m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
                i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
                if (snd_mask_single(m) && snd_interval_single(i)) {
-                       err = substream->ops->ioctl(substream,
-                                       SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
+                       err = snd_pcm_ops_ioctl(substream,
+                                               SNDRV_PCM_IOCTL1_FIFO_SIZE,
+                                               params);
                        if (err < 0)
                                return err;
                }
@@ -555,6 +568,15 @@ static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
 #endif
 }
 
+static void snd_pcm_sync_stop(struct snd_pcm_substream *substream)
+{
+       if (substream->runtime->stop_operating) {
+               substream->runtime->stop_operating = false;
+               if (substream->ops->sync_stop)
+                       substream->ops->sync_stop(substream);
+       }
+}
+
 /**
  * snd_pcm_hw_param_choose - choose a configuration defined by @params
  * @pcm: PCM instance
@@ -647,6 +669,8 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
                if (atomic_read(&substream->mmap_count))
                        return -EBADFD;
 
+       snd_pcm_sync_stop(substream);
+
        params->rmask = ~0U;
        err = snd_pcm_hw_refine(substream, params);
        if (err < 0)
@@ -660,6 +684,14 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
        if (err < 0)
                goto _error;
 
+       if (substream->managed_buffer_alloc) {
+               err = snd_pcm_lib_malloc_pages(substream,
+                                              params_buffer_bytes(params));
+               if (err < 0)
+                       goto _error;
+               runtime->buffer_changed = err > 0;
+       }
+
        if (substream->ops->hw_params != NULL) {
                err = substream->ops->hw_params(substream, params);
                if (err < 0)
@@ -721,6 +753,8 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
        snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
        if (substream->ops->hw_free != NULL)
                substream->ops->hw_free(substream);
+       if (substream->managed_buffer_alloc)
+               snd_pcm_lib_free_pages(substream);
        return err;
 }
 
@@ -765,8 +799,11 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
        snd_pcm_stream_unlock_irq(substream);
        if (atomic_read(&substream->mmap_count))
                return -EBADFD;
+       snd_pcm_sync_stop(substream);
        if (substream->ops->hw_free)
                result = substream->ops->hw_free(substream);
+       if (substream->managed_buffer_alloc)
+               snd_pcm_lib_free_pages(substream);
        snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
        pm_qos_remove_request(&substream->latency_pm_qos_req);
        return result;
@@ -957,7 +994,7 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
                return -EINVAL;
        memset(info, 0, sizeof(*info));
        info->channel = channel;
-       return substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
+       return snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
 }
 
 static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
@@ -1288,6 +1325,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
                runtime->status->state = state;
                snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
        }
+       runtime->stop_operating = true;
        wake_up(&runtime->sleep);
        wake_up(&runtime->tsleep);
 }
@@ -1564,6 +1602,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
        snd_pcm_trigger_tstamp(substream);
        runtime->status->state = runtime->status->suspended_state;
        snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
+       snd_pcm_sync_stop(substream);
 }
 
 static const struct action_ops snd_pcm_action_resume = {
@@ -1633,7 +1672,7 @@ static int snd_pcm_pre_reset(struct snd_pcm_substream *substream, int state)
 static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-       int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
+       int err = snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
        if (err < 0)
                return err;
        runtime->hw_ptr_base = 0;
@@ -1684,6 +1723,7 @@ static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream,
 static int snd_pcm_do_prepare(struct snd_pcm_substream *substream, int state)
 {
        int err;
+       snd_pcm_sync_stop(substream);
        err = substream->ops->prepare(substream);
        if (err < 0)
                return err;
@@ -3334,7 +3374,18 @@ static inline struct page *
 snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
 {
        void *vaddr = substream->runtime->dma_area + ofs;
-       return virt_to_page(vaddr);
+
+       switch (substream->dma_buffer.dev.type) {
+#ifdef CONFIG_SND_DMA_SGBUF
+       case SNDRV_DMA_TYPE_DEV_SG:
+       case SNDRV_DMA_TYPE_DEV_UC_SG:
+               return snd_pcm_sgbuf_ops_page(substream, ofs);
+#endif /* CONFIG_SND_DMA_SGBUF */
+       case SNDRV_DMA_TYPE_VMALLOC:
+               return vmalloc_to_page(vaddr);
+       default:
+               return virt_to_page(vaddr);
+       }
 }
 
 /*
@@ -3403,7 +3454,8 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
 #endif /* CONFIG_GENERIC_ALLOCATOR */
 #ifndef CONFIG_X86 /* for avoiding warnings arch/x86/mm/pat.c */
        if (IS_ENABLED(CONFIG_HAS_DMA) && !substream->ops->page &&
-           substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+           (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV ||
+            substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_UC))
                return dma_mmap_coherent(substream->dma_buffer.dev.dev,
                                         area,
                                         substream->runtime->dma_area,