ASoC: rsnd: add Audio DMAC peri peri support rework
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Fri, 20 Feb 2015 10:27:42 +0000 (10:27 +0000)
committerMark Brown <broonie@kernel.org>
Sat, 7 Mar 2015 15:04:30 +0000 (15:04 +0000)
Renesas R-Car sound (= rsnd) needs 2 DMAC which are called as
Audio DMAC (= 1st DMAC) and Audio DMAC peri peri (2nd DMAC).
And rsnd had assumed that 1st / 2nd DMACs are implemented as DMAEngine.
But, in result of DMA ML discussion, 2nd DMAC was concluded that it is
not a general purpose DMAC (2nd DMAC is for Device to Device inside
sound system). Additionally, current DMAEngine can't support Device to
Device, and we don't have correct DT bindings for it at this point.
So the easiest solution for it is that move it from DMAEngine to rsnd
driver.
Audio DMAC peri peri is controlled from sound driver without DMAEngine
by this patch.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/dma.c
sound/soc/sh/rcar/rsnd.h

index 9beea9ba338a0145a364de92bed3e1b37847e46c..3b6e21948c71333c39fa448fb77314a2ceed3b4d 100644 (file)
@@ -974,6 +974,7 @@ static int rsnd_probe(struct platform_device *pdev)
                            const struct rsnd_of_data *of_data,
                            struct rsnd_priv *priv) = {
                rsnd_gen_probe,
+               rsnd_dma_probe,
                rsnd_ssi_probe,
                rsnd_src_probe,
                rsnd_dvc_probe,
index c911c079fdd094f3c485eb926f4808c7c0ca9322..a01bb8c6b06808b110a54a59862eb3e7dc1f03ae 100644 (file)
@@ -8,8 +8,29 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#include <linux/delay.h>
 #include "rsnd.h"
 
+/*
+ * Audio DMAC peri peri register
+ */
+#define PDMASAR                0x00
+#define PDMADAR                0x04
+#define PDMACHCR       0x0c
+
+/* PDMACHCR */
+#define PDMACHCR_DE            (1 << 0)
+
+struct rsnd_dma_ctrl {
+       void __iomem *base;
+       int dmapp_num;
+};
+
+#define rsnd_priv_to_dmac(p)   ((struct rsnd_dma_ctrl *)(p)->dma)
+
+/*
+ *             Audio DMAC
+ */
 static void rsnd_dmaen_complete(void *data)
 {
        struct rsnd_dma *dma = (struct rsnd_dma *)data;
@@ -108,6 +129,8 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
                return -EIO;
        }
 
+       dev_dbg(dev, "Audio DMAC init\n");
+
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);
 
@@ -167,6 +190,150 @@ static struct rsnd_dma_ops rsnd_dmaen_ops = {
        .quit   = rsnd_dmaen_quit,
 };
 
+/*
+ *             Audio DMAC peri peri
+ */
+static const u8 gen2_id_table_ssiu[] = {
+       0x00, /* SSI00 */
+       0x04, /* SSI10 */
+       0x08, /* SSI20 */
+       0x0c, /* SSI3  */
+       0x0d, /* SSI4  */
+       0x0e, /* SSI5  */
+       0x0f, /* SSI6  */
+       0x10, /* SSI7  */
+       0x11, /* SSI8  */
+       0x12, /* SSI90 */
+};
+static const u8 gen2_id_table_scu[] = {
+       0x2d, /* SCU_SRCI0 */
+       0x2e, /* SCU_SRCI1 */
+       0x2f, /* SCU_SRCI2 */
+       0x30, /* SCU_SRCI3 */
+       0x31, /* SCU_SRCI4 */
+       0x32, /* SCU_SRCI5 */
+       0x33, /* SCU_SRCI6 */
+       0x34, /* SCU_SRCI7 */
+       0x35, /* SCU_SRCI8 */
+       0x36, /* SCU_SRCI9 */
+};
+static const u8 gen2_id_table_cmd[] = {
+       0x37, /* SCU_CMD0 */
+       0x38, /* SCU_CMD1 */
+};
+
+static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod)
+{
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+       struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+       struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+       const u8 *entry = NULL;
+       int id = rsnd_mod_id(mod);
+       int size = 0;
+
+       if (mod == ssi) {
+               entry = gen2_id_table_ssiu;
+               size = ARRAY_SIZE(gen2_id_table_ssiu);
+       } else if (mod == src) {
+               entry = gen2_id_table_scu;
+               size = ARRAY_SIZE(gen2_id_table_scu);
+       } else if (mod == dvc) {
+               entry = gen2_id_table_cmd;
+               size = ARRAY_SIZE(gen2_id_table_cmd);
+       }
+
+       if (!entry)
+               return 0xFF;
+
+       if (size <= id)
+               return 0xFF;
+
+       return entry[id];
+}
+
+static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from,
+                              struct rsnd_mod *mod_to)
+{
+       return  (rsnd_dmapp_get_id(mod_from) << 24) +
+               (rsnd_dmapp_get_id(mod_to) << 16);
+}
+
+#define rsnd_dmapp_addr(dmac, dma, reg) \
+       (dmac->base + 0x20 + (0x10 * dma->dmapp_id) + reg)
+static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
+{
+       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data);
+
+       iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg));
+}
+
+static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
+{
+       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+
+       return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
+}
+
+static void rsnd_dmapp_stop(struct rsnd_dma *dma)
+{
+       int i;
+
+       rsnd_dmapp_write(dma, 0, PDMACHCR);
+
+       for (i = 0; i < 1024; i++) {
+               if (0 == rsnd_dmapp_read(dma, PDMACHCR))
+                       return;
+               udelay(1);
+       }
+}
+
+static void rsnd_dmapp_start(struct rsnd_dma *dma)
+{
+       rsnd_dmapp_write(dma, dma->src_addr,    PDMASAR);
+       rsnd_dmapp_write(dma, dma->dst_addr,    PDMADAR);
+       rsnd_dmapp_write(dma, dma->chcr,        PDMACHCR);
+}
+
+static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
+                          struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
+{
+       struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       dev_dbg(dev, "Audio DMAC peri peri init\n");
+
+       dma->dmapp_id = dmac->dmapp_num;
+       dma->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE;
+
+       dmac->dmapp_num++;
+
+       rsnd_dmapp_stop(dma);
+
+       dev_dbg(dev, "id/src/dst/chcr = %d/%x/%x/%08x\n",
+               dma->dmapp_id, dma->src_addr, dma->dst_addr, dma->chcr);
+
+       return 0;
+}
+
+static struct rsnd_dma_ops rsnd_dmapp_ops = {
+       .start  = rsnd_dmapp_start,
+       .stop   = rsnd_dmapp_stop,
+       .init   = rsnd_dmapp_init,
+       .quit   = rsnd_dmapp_stop,
+};
+
+/*
+ *             Common DMAC Interface
+ */
+
 /*
  *     DMA read/write register offset
  *
@@ -367,7 +534,49 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
        dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1);
        dma->dst_addr = rsnd_dma_addr(priv, mod_to,   is_play, 0);
 
-       dma->ops = &rsnd_dmaen_ops;
+       /* for Gen2 */
+       if (mod_from && mod_to)
+               dma->ops = &rsnd_dmapp_ops;
+       else
+               dma->ops = &rsnd_dmaen_ops;
+
+       /* for Gen1, overwrite */
+       if (rsnd_is_gen1(priv))
+               dma->ops = &rsnd_dmaen_ops;
 
        return dma->ops->init(priv, dma, id, mod_from, mod_to);
 }
+
+int rsnd_dma_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
+                  struct rsnd_priv *priv)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_dma_ctrl *dmac;
+       struct resource *res;
+
+       /*
+        * for Gen1
+        */
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /*
+        * for Gen2
+        */
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp");
+       dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL);
+       if (!dmac || !res) {
+               dev_err(dev, "dma allocate failed\n");
+               return -ENOMEM;
+       }
+
+       dmac->dmapp_num = 0;
+       dmac->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(dmac->base))
+               return PTR_ERR(dmac->base);
+
+       priv->dma = dmac;
+
+       return 0;
+}
index c7299f74cf836276dd73649605f9886c0b775716..9e67142c82bd20be5c044137b839357adfdb54de 100644 (file)
@@ -184,6 +184,8 @@ struct rsnd_dma {
        struct rsnd_dma_ops     *ops;
        enum dma_transfer_direction dir;
        dma_addr_t              addr;
+       int                     dmapp_id;
+       u32                     chcr;
        dma_addr_t              src_addr;
        dma_addr_t              dst_addr;
 };
@@ -192,7 +194,9 @@ void rsnd_dma_start(struct rsnd_dma *dma);
 void rsnd_dma_stop(struct rsnd_dma *dma);
 int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id);
 void  rsnd_dma_quit(struct rsnd_dma *dma);
-
+int rsnd_dma_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
+                  struct rsnd_priv *priv);
 
 #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
 
@@ -395,6 +399,11 @@ struct rsnd_priv {
         */
        void *adg;
 
+       /*
+        * below value will be filled on rsnd_dma_probe()
+        */
+       void *dma;
+
        /*
         * below value will be filled on rsnd_ssi_probe()
         */