summaryrefslogtreecommitdiff
path: root/sound/soc/omap/omap-mcpdm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/omap/omap-mcpdm.c')
-rw-r--r--sound/soc/omap/omap-mcpdm.c74
1 files changed, 69 insertions, 5 deletions
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
index b837265ac3e9..e7cdc51fd806 100644
--- a/sound/soc/omap/omap-mcpdm.c
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -31,6 +31,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/of_device.h>
@@ -54,6 +55,7 @@ struct omap_mcpdm {
unsigned long phys_base;
void __iomem *io_base;
int irq;
+ struct clk *pdmclk;
struct mutex mutex;
@@ -66,6 +68,9 @@ struct omap_mcpdm {
/* McPDM needs to be restarted due to runtime reconfiguration */
bool restart;
+ /* pm state for suspend/resume handling */
+ int pm_active_count;
+
struct snd_dmaengine_dai_dma_data dma_data[2];
};
@@ -173,6 +178,10 @@ static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm)
*/
static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm)
{
+ u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
+
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
+
omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET,
MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL |
MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
@@ -258,12 +267,9 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
mutex_lock(&mcpdm->mutex);
- if (!dai->active) {
- u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
-
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
+ if (!dai->active)
omap_mcpdm_open_streams(mcpdm);
- }
+
mutex_unlock(&mcpdm->mutex);
return 0;
@@ -384,6 +390,7 @@ static int omap_mcpdm_probe(struct snd_soc_dai *dai)
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
int ret;
+ clk_prepare_enable(mcpdm->pdmclk);
pm_runtime_enable(mcpdm->dev);
/* Disable lines while request is ongoing */
@@ -418,8 +425,54 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
pm_runtime_disable(mcpdm->dev);
+ clk_disable_unprepare(mcpdm->pdmclk);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int omap_mcpdm_suspend(struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ if (dai->active) {
+ omap_mcpdm_stop(mcpdm);
+ omap_mcpdm_close_streams(mcpdm);
+ }
+
+ mcpdm->pm_active_count = 0;
+ while (pm_runtime_active(mcpdm->dev)) {
+ pm_runtime_put_sync(mcpdm->dev);
+ mcpdm->pm_active_count++;
+ }
+
+ clk_disable_unprepare(mcpdm->pdmclk);
+
+ return 0;
+}
+
+static int omap_mcpdm_resume(struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ clk_prepare_enable(mcpdm->pdmclk);
+
+ if (mcpdm->pm_active_count) {
+ while (mcpdm->pm_active_count--)
+ pm_runtime_get_sync(mcpdm->dev);
+
+ if (dai->active) {
+ omap_mcpdm_open_streams(mcpdm);
+ omap_mcpdm_start(mcpdm);
+ }
+ }
+
+
return 0;
}
+#else
+#define omap_mcpdm_suspend NULL
+#define omap_mcpdm_resume NULL
+#endif
#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
#define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE
@@ -427,6 +480,8 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver omap_mcpdm_dai = {
.probe = omap_mcpdm_probe,
.remove = omap_mcpdm_remove,
+ .suspend = omap_mcpdm_suspend,
+ .resume = omap_mcpdm_resume,
.probe_order = SND_SOC_COMP_ORDER_LATE,
.remove_order = SND_SOC_COMP_ORDER_EARLY,
.playback = {
@@ -494,6 +549,15 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
mcpdm->dev = &pdev->dev;
+ mcpdm->pdmclk = devm_clk_get(&pdev->dev, "pdmclk");
+ if (IS_ERR(mcpdm->pdmclk)) {
+ if (PTR_ERR(mcpdm->pdmclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_warn(&pdev->dev, "Error getting pdmclk (%ld)!\n",
+ PTR_ERR(mcpdm->pdmclk));
+ mcpdm->pdmclk = NULL;
+ }
+
ret = devm_snd_soc_register_component(&pdev->dev,
&omap_mcpdm_component,
&omap_mcpdm_dai, 1);