|
@@ -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;
|
|
|
}
|
|
|
|