Merge tag 'msm-fixes-2018-12-04' of https://gitlab.freedesktop.org/seanpaul/dpu-stagi...
[sfrench/cifs-2.6.git] / sound / soc / omap / omap-mcpdm.c
index 4c1be36c22075b3b449325dfc44eccd565c50f1a..7d5bdc5a2890369eea9ea84ae7b0b5ed30d3e334 100644 (file)
@@ -54,6 +54,8 @@ struct omap_mcpdm {
        unsigned long phys_base;
        void __iomem *io_base;
        int irq;
+       struct pm_qos_request pm_qos_req;
+       int latency[2];
 
        struct mutex mutex;
 
@@ -277,6 +279,9 @@ static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+       int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+       int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
 
        mutex_lock(&mcpdm->mutex);
 
@@ -289,6 +294,14 @@ static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
                }
        }
 
+       if (mcpdm->latency[stream2])
+               pm_qos_update_request(&mcpdm->pm_qos_req,
+                                     mcpdm->latency[stream2]);
+       else if (mcpdm->latency[stream1])
+               pm_qos_remove_request(&mcpdm->pm_qos_req);
+
+       mcpdm->latency[stream1] = 0;
+
        mutex_unlock(&mcpdm->mutex);
 }
 
@@ -300,7 +313,7 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
        int stream = substream->stream;
        struct snd_dmaengine_dai_dma_data *dma_data;
        u32 threshold;
-       int channels;
+       int channels, latency;
        int link_mask = 0;
 
        channels = params_channels(params);
@@ -344,14 +357,25 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
 
                dma_data->maxburst =
                                (MCPDM_DN_THRES_MAX - threshold) * channels;
+               latency = threshold;
        } else {
                /* If playback is not running assume a stereo stream to come */
                if (!mcpdm->config[!stream].link_mask)
                        mcpdm->config[!stream].link_mask = (0x3 << 3);
 
                dma_data->maxburst = threshold * channels;
+               latency = (MCPDM_DN_THRES_MAX - threshold);
        }
 
+       /*
+        * The DMA must act to a DMA request within latency time (usec) to avoid
+        * under/overflow
+        */
+       mcpdm->latency[stream] = latency * USEC_PER_SEC / params_rate(params);
+
+       if (!mcpdm->latency[stream])
+               mcpdm->latency[stream] = 10;
+
        /* Check if we need to restart McPDM with this stream */
        if (mcpdm->config[stream].link_mask &&
            mcpdm->config[stream].link_mask != link_mask)
@@ -366,6 +390,20 @@ static int omap_mcpdm_prepare(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+       struct pm_qos_request *pm_qos_req = &mcpdm->pm_qos_req;
+       int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+       int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+       int latency = mcpdm->latency[stream2];
+
+       /* Prevent omap hardware from hitting off between FIFO fills */
+       if (!latency || mcpdm->latency[stream1] < latency)
+               latency = mcpdm->latency[stream1];
+
+       if (pm_qos_request_active(pm_qos_req))
+               pm_qos_update_request(pm_qos_req, latency);
+       else if (latency)
+               pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency);
 
        if (!omap_mcpdm_active(mcpdm)) {
                omap_mcpdm_start(mcpdm);
@@ -427,6 +465,9 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
        free_irq(mcpdm->irq, (void *)mcpdm);
        pm_runtime_disable(mcpdm->dev);
 
+       if (pm_qos_request_active(&mcpdm->pm_qos_req))
+               pm_qos_remove_request(&mcpdm->pm_qos_req);
+
        return 0;
 }