]> Pileus Git - ~andy/linux/blobdiff - sound/soc/omap/omap-mcpdm.c
Merge tag 'mfd-3.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd...
[~andy/linux] / sound / soc / omap / omap-mcpdm.c
index 5ca11bdac21e3b8b7d37ba1ee1978c6cf477cb15..eb05c7ed6d053519a13ec989efd81c890aa10c72 100644 (file)
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
 
 #include "omap-mcpdm.h"
-#include "omap-pcm.h"
 
-#define OMAP44XX_MCPDM_L3_BASE         0x49032000
+struct mcpdm_link_config {
+       u32 link_mask; /* channel mask for the direction */
+       u32 threshold; /* FIFO threshold */
+};
 
 struct omap_mcpdm {
        struct device *dev;
@@ -53,29 +56,22 @@ struct omap_mcpdm {
 
        struct mutex mutex;
 
-       /* channel data */
-       u32 dn_channels;
-       u32 up_channels;
-
-       /* McPDM FIFO thresholds */
-       u32 dn_threshold;
-       u32 up_threshold;
+       /* Playback/Capture configuration */
+       struct mcpdm_link_config config[2];
 
        /* McPDM dn offsets for rx1, and 2 channels */
        u32 dn_rx_offset;
+
+       /* McPDM needs to be restarted due to runtime reconfiguration */
+       bool restart;
+
+       struct snd_dmaengine_dai_dma_data dma_data[2];
+       unsigned int dma_req[2];
 };
 
 /*
  * Stream DMA parameters
  */
-static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = {
-       {
-               .name = "Audio playback",
-       },
-       {
-               .name = "Audio capture",
-       },
-};
 
 static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val)
 {
@@ -130,11 +126,12 @@ static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) {}
 static void omap_mcpdm_start(struct omap_mcpdm *mcpdm)
 {
        u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
+       u32 link_mask = mcpdm->config[0].link_mask | mcpdm->config[1].link_mask;
 
        ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
        omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
 
-       ctrl |= mcpdm->dn_channels | mcpdm->up_channels;
+       ctrl |= link_mask;
        omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
 
        ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
@@ -148,11 +145,12 @@ static void omap_mcpdm_start(struct omap_mcpdm *mcpdm)
 static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm)
 {
        u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
+       u32 link_mask = MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK;
 
        ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
        omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
 
-       ctrl &= ~(mcpdm->dn_channels | mcpdm->up_channels);
+       ctrl &= ~(link_mask);
        omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
 
        ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
@@ -188,8 +186,10 @@ static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm)
                omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset);
        }
 
-       omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN, mcpdm->dn_threshold);
-       omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP, mcpdm->up_threshold);
+       omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN,
+                        mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold);
+       omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP,
+                        mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold);
 
        omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_SET,
                        MCPDM_DMA_DN_ENABLE | MCPDM_DMA_UP_ENABLE);
@@ -267,7 +267,7 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
        mutex_unlock(&mcpdm->mutex);
 
        snd_soc_dai_set_dma_data(dai, substream,
-                                &omap_mcpdm_dai_dma_params[substream->stream]);
+                                &mcpdm->dma_data[substream->stream]);
 
        return 0;
 }
@@ -283,6 +283,8 @@ static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
                if (omap_mcpdm_active(mcpdm)) {
                        omap_mcpdm_stop(mcpdm);
                        omap_mcpdm_close_streams(mcpdm);
+                       mcpdm->config[0].link_mask = 0;
+                       mcpdm->config[1].link_mask = 0;
                }
        }
 
@@ -295,7 +297,8 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
 {
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
        int stream = substream->stream;
-       struct omap_pcm_dma_data *dma_data;
+       struct snd_dmaengine_dai_dma_data *dma_data;
+       u32 threshold;
        int channels;
        int link_mask = 0;
 
@@ -325,16 +328,32 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
 
        dma_data = snd_soc_dai_get_dma_data(dai, substream);
 
+       threshold = mcpdm->config[stream].threshold;
        /* Configure McPDM channels, and DMA packet size */
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               mcpdm->dn_channels = link_mask << 3;
-               dma_data->packet_size =
-                       (MCPDM_DN_THRES_MAX - mcpdm->dn_threshold) * channels;
+               link_mask <<= 3;
+
+               /* If capture is not running assume a stereo stream to come */
+               if (!mcpdm->config[!stream].link_mask)
+                       mcpdm->config[!stream].link_mask = 0x3;
+
+               dma_data->maxburst =
+                               (MCPDM_DN_THRES_MAX - threshold) * channels;
        } else {
-               mcpdm->up_channels = link_mask << 0;
-               dma_data->packet_size = mcpdm->up_threshold * channels;
+               /* 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;
        }
 
+       /* Check if we need to restart McPDM with this stream */
+       if (mcpdm->config[stream].link_mask &&
+           mcpdm->config[stream].link_mask != link_mask)
+               mcpdm->restart = true;
+
+       mcpdm->config[stream].link_mask = link_mask;
+
        return 0;
 }
 
@@ -346,6 +365,11 @@ static int omap_mcpdm_prepare(struct snd_pcm_substream *substream,
        if (!omap_mcpdm_active(mcpdm)) {
                omap_mcpdm_start(mcpdm);
                omap_mcpdm_reg_dump(mcpdm);
+       } else if (mcpdm->restart) {
+               omap_mcpdm_stop(mcpdm);
+               omap_mcpdm_start(mcpdm);
+               mcpdm->restart = false;
+               omap_mcpdm_reg_dump(mcpdm);
        }
 
        return 0;
@@ -369,7 +393,7 @@ static int omap_mcpdm_probe(struct snd_soc_dai *dai)
        pm_runtime_get_sync(mcpdm->dev);
        omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00);
 
-       ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
+       ret = devm_request_irq(mcpdm->dev, mcpdm->irq, omap_mcpdm_irq_handler,
                                0, "McPDM", (void *)mcpdm);
 
        pm_runtime_put_sync(mcpdm->dev);
@@ -380,8 +404,9 @@ static int omap_mcpdm_probe(struct snd_soc_dai *dai)
        }
 
        /* Configure McPDM threshold values */
-       mcpdm->dn_threshold = 2;
-       mcpdm->up_threshold = MCPDM_UP_THRES_MAX - 3;
+       mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold = 2;
+       mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold =
+                                                       MCPDM_UP_THRES_MAX - 3;
        return ret;
 }
 
@@ -389,7 +414,6 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
 {
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
 
-       free_irq(mcpdm->irq, (void *)mcpdm);
        pm_runtime_disable(mcpdm->dev);
 
        return 0;
@@ -420,6 +444,10 @@ static struct snd_soc_dai_driver omap_mcpdm_dai = {
        .ops = &omap_mcpdm_dai_ops,
 };
 
+static const struct snd_soc_component_driver omap_mcpdm_component = {
+       .name           = "omap-mcpdm",
+};
+
 void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd,
                                    u8 rx1, u8 rx2)
 {
@@ -446,33 +474,30 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
        if (res == NULL)
                return -ENOMEM;
 
-       omap_mcpdm_dai_dma_params[0].port_addr = res->start + MCPDM_REG_DN_DATA;
-       omap_mcpdm_dai_dma_params[1].port_addr = res->start + MCPDM_REG_UP_DATA;
+       mcpdm->dma_data[0].addr = res->start + MCPDM_REG_DN_DATA;
+       mcpdm->dma_data[1].addr = res->start + MCPDM_REG_UP_DATA;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "dn_link");
        if (!res)
                return -ENODEV;
 
-       omap_mcpdm_dai_dma_params[0].dma_req = res->start;
+       mcpdm->dma_req[0] = res->start;
+       mcpdm->dma_data[0].filter_data = &mcpdm->dma_req[0];
 
        res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "up_link");
        if (!res)
                return -ENODEV;
 
-       omap_mcpdm_dai_dma_params[1].dma_req = res->start;
+       mcpdm->dma_req[1] = res->start;
+       mcpdm->dma_data[1].filter_data = &mcpdm->dma_req[1];
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
        if (res == NULL)
                return -ENOMEM;
 
-       if (!devm_request_mem_region(&pdev->dev, res->start,
-                                    resource_size(res), "McPDM"))
-               return -EBUSY;
-
-       mcpdm->io_base = devm_ioremap(&pdev->dev, res->start,
-                                     resource_size(res));
-       if (!mcpdm->io_base)
-               return -ENOMEM;
+       mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(mcpdm->io_base))
+               return PTR_ERR(mcpdm->io_base);
 
        mcpdm->irq = platform_get_irq(pdev, 0);
        if (mcpdm->irq < 0)
@@ -480,12 +505,13 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
 
        mcpdm->dev = &pdev->dev;
 
-       return snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai);
+       return snd_soc_register_component(&pdev->dev, &omap_mcpdm_component,
+                                         &omap_mcpdm_dai, 1);
 }
 
 static int asoc_mcpdm_remove(struct platform_device *pdev)
 {
-       snd_soc_unregister_dai(&pdev->dev);
+       snd_soc_unregister_component(&pdev->dev);
        return 0;
 }