ASoC: rsnd: add DeviceTree support
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tue, 18 Mar 2014 02:29:55 +0000 (19:29 -0700)
committerMark Brown <broonie@linaro.org>
Fri, 21 Mar 2014 18:14:56 +0000 (18:14 +0000)
Support for loading the Renesas R-Car sound driver via DeviceTree.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Documentation/devicetree/bindings/sound/renesas,rsnd.txt [new file with mode: 0644]
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c

diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
new file mode 100644 (file)
index 0000000..7c6d33f
--- /dev/null
@@ -0,0 +1,96 @@
+Renesas R-Car sound
+
+Required properties:
+- compatible                   : "renesas,rcar_sound-gen1" if generation1
+                                 "renesas,rcar_sound-gen2" if generation2
+- reg                          : Should contain the register physical address.
+                                 required register is
+                                  SRU/ADG/SSI      if generation1
+                                  SRU/ADG/SSIU/SSI if generation2
+- rcar_sound,ssi               : SSI subnode
+- rcar_sound,scu               : SCU subnode
+- rcar_sound,dai               : DAI subnode
+
+SSI subnode properties:
+- interrupts                   : Should contain SSI interrupt for PIO transfer
+- shared-pin                   : if shared clock pin
+
+DAI subnode properties:
+- playback                     : list of playback modules
+- capture                      : list of capture  modules
+
+Example:
+
+rcar_sound: rcar_sound@0xffd90000 {
+       #sound-dai-cells = <1>;
+       compatible = "renesas,rcar_sound-gen2";
+       reg =   <0 0xec500000 0 0x1000>, /* SCU */
+               <0 0xec5a0000 0 0x100>,  /* ADG */
+               <0 0xec540000 0 0x1000>, /* SSIU */
+               <0 0xec541000 0 0x1280>; /* SSI */
+
+       rcar_sound,src {
+               src0: src@0 { };
+               src1: src@1 { };
+               src2: src@2 { };
+               src3: src@3 { };
+               src4: src@4 { };
+               src5: src@5 { };
+               src6: src@6 { };
+               src7: src@7 { };
+               src8: src@8 { };
+               src9: src@9 { };
+       };
+
+       rcar_sound,ssi {
+               ssi0: ssi@0 {
+                       interrupts = <0 370 IRQ_TYPE_LEVEL_HIGH>;
+               };
+               ssi1: ssi@1 {
+                       interrupts = <0 371 IRQ_TYPE_LEVEL_HIGH>;
+               };
+               ssi2: ssi@2 {
+                       interrupts = <0 372 IRQ_TYPE_LEVEL_HIGH>;
+               };
+               ssi3: ssi@3 {
+                       interrupts = <0 373 IRQ_TYPE_LEVEL_HIGH>;
+               };
+               ssi4: ssi@4 {
+                       interrupts = <0 374 IRQ_TYPE_LEVEL_HIGH>;
+               };
+               ssi5: ssi@5 {
+                       interrupts = <0 375 IRQ_TYPE_LEVEL_HIGH>;
+               };
+               ssi6: ssi@6 {
+                       interrupts = <0 376 IRQ_TYPE_LEVEL_HIGH>;
+               };
+               ssi7: ssi@7 {
+                       interrupts = <0 377 IRQ_TYPE_LEVEL_HIGH>;
+               };
+               ssi8: ssi@8 {
+                       interrupts = <0 378 IRQ_TYPE_LEVEL_HIGH>;
+               };
+               ssi9: ssi@9 {
+                       interrupts = <0 379 IRQ_TYPE_LEVEL_HIGH>;
+               };
+       };
+
+       rcar_sound,dai {
+               dai0 {
+                       playback = <&ssi5 &src5>;
+                       capture  = <&ssi6>;
+               };
+               dai1 {
+                       playback = <&ssi3>;
+               };
+               dai2 {
+                       capture  = <&ssi4>;
+               };
+               dai3 {
+                       playback = <&ssi7>;
+               };
+               dai4 {
+                       capture  = <&ssi8>;
+               };
+       };
+};
index 953f1cc..69c4426 100644 (file)
@@ -392,6 +392,7 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
 }
 
 int rsnd_adg_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
                   struct rsnd_priv *priv)
 {
        struct rsnd_adg *adg;
index 6a1b45d..e77f771 100644 (file)
 #define RSND_RATES SNDRV_PCM_RATE_8000_96000
 #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
+static struct rsnd_of_data rsnd_of_data_gen1 = {
+       .flags = RSND_GEN1,
+};
+
+static struct rsnd_of_data rsnd_of_data_gen2 = {
+       .flags = RSND_GEN2,
+};
+
+static struct of_device_id rsnd_of_match[] = {
+       { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
+       { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rsnd_of_match);
+
 /*
  *     rsnd_platform functions
  */
@@ -620,7 +635,92 @@ static int rsnd_path_init(struct rsnd_priv *priv,
        return ret;
 }
 
+static void rsnd_of_parse_dai(struct platform_device *pdev,
+                             const struct rsnd_of_data *of_data,
+                             struct rsnd_priv *priv)
+{
+       struct device_node *dai_node,   *dai_np;
+       struct device_node *ssi_node,   *ssi_np;
+       struct device_node *src_node,   *src_np;
+       struct device_node *playback, *capture;
+       struct rsnd_dai_platform_info *dai_info;
+       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+       struct device *dev = &pdev->dev;
+       int nr, i;
+       int dai_i, ssi_i, src_i;
+
+       if (!of_data)
+               return;
+
+       dai_node = of_get_child_by_name(dev->of_node, "rcar_sound,dai");
+       if (!dai_node)
+               return;
+
+       nr = of_get_child_count(dai_node);
+       if (!nr)
+               return;
+
+       dai_info = devm_kzalloc(dev,
+                               sizeof(struct rsnd_dai_platform_info) * nr,
+                               GFP_KERNEL);
+       if (!dai_info) {
+               dev_err(dev, "dai info allocation error\n");
+               return;
+       }
+
+       info->dai_info_nr       = nr;
+       info->dai_info          = dai_info;
+
+       ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
+       src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
+
+#define mod_parse(name)                                                        \
+if (name##_node) {                                                     \
+       struct rsnd_##name##_platform_info *name##_info;                \
+                                                                       \
+       name##_i = 0;                                                   \
+       for_each_child_of_node(name##_node, name##_np) {                \
+               name##_info = info->name##_info + name##_i;             \
+                                                                       \
+               if (name##_np == playback)                              \
+                       dai_info->playback.name = name##_info;          \
+               if (name##_np == capture)                               \
+                       dai_info->capture.name = name##_info;           \
+                                                                       \
+               name##_i++;                                             \
+       }                                                               \
+}
+
+       /*
+        * parse all dai
+        */
+       dai_i = 0;
+       for_each_child_of_node(dai_node, dai_np) {
+               dai_info = info->dai_info + dai_i;
+
+               for (i = 0;; i++) {
+
+                       playback = of_parse_phandle(dai_np, "playback", i);
+                       capture  = of_parse_phandle(dai_np, "capture", i);
+
+                       if (!playback && !capture)
+                               break;
+
+                       mod_parse(ssi);
+                       mod_parse(src);
+
+                       if (playback)
+                               of_node_put(playback);
+                       if (capture)
+                               of_node_put(capture);
+               }
+
+               dai_i++;
+       }
+}
+
 static int rsnd_dai_probe(struct platform_device *pdev,
+                         const struct rsnd_of_data *of_data,
                          struct rsnd_priv *priv)
 {
        struct snd_soc_dai_driver *drv;
@@ -628,13 +728,16 @@ static int rsnd_dai_probe(struct platform_device *pdev,
        struct rsnd_dai *rdai;
        struct rsnd_mod *pmod, *cmod;
        struct device *dev = rsnd_priv_to_dev(priv);
-       int dai_nr = info->dai_info_nr;
+       int dai_nr;
        int i;
 
+       rsnd_of_parse_dai(pdev, of_data, priv);
+
        /*
         * dai_nr should be set via dai_info_nr,
         * but allow it to keeping compatible
         */
+       dai_nr = info->dai_info_nr;
        if (!dai_nr) {
                /* get max dai nr */
                for (dai_nr = 0; dai_nr < 32; dai_nr++) {
@@ -802,7 +905,10 @@ static int rsnd_probe(struct platform_device *pdev)
        struct rsnd_priv *priv;
        struct device *dev = &pdev->dev;
        struct rsnd_dai *rdai;
+       const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev);
+       const struct rsnd_of_data *of_data;
        int (*probe_func[])(struct platform_device *pdev,
+                           const struct rsnd_of_data *of_data,
                            struct rsnd_priv *priv) = {
                rsnd_gen_probe,
                rsnd_ssi_probe,
@@ -812,7 +918,16 @@ static int rsnd_probe(struct platform_device *pdev)
        };
        int ret, i;
 
-       info = pdev->dev.platform_data;
+       info = NULL;
+       of_data = NULL;
+       if (of_id) {
+               info = devm_kzalloc(&pdev->dev,
+                                   sizeof(struct rcar_snd_info), GFP_KERNEL);
+               of_data = of_id->data;
+       } else {
+               info = pdev->dev.platform_data;
+       }
+
        if (!info) {
                dev_err(dev, "driver needs R-Car sound information\n");
                return -ENODEV;
@@ -835,7 +950,7 @@ static int rsnd_probe(struct platform_device *pdev)
         *      init each module
         */
        for (i = 0; i < ARRAY_SIZE(probe_func); i++) {
-               ret = probe_func[i](pdev, priv);
+               ret = probe_func[i](pdev, of_data, priv);
                if (ret)
                        return ret;
        }
@@ -903,6 +1018,7 @@ static int rsnd_remove(struct platform_device *pdev)
 static struct platform_driver rsnd_driver = {
        .driver = {
                .name   = "rcar_sound",
+               .of_match_table = rsnd_of_match,
        },
        .probe          = rsnd_probe,
        .remove         = rsnd_remove,
index 9094970..50a1ef3 100644 (file)
@@ -359,13 +359,28 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
 /*
  *             Gen
  */
+static void rsnd_of_parse_gen(struct platform_device *pdev,
+                             const struct rsnd_of_data *of_data,
+                             struct rsnd_priv *priv)
+{
+       struct rcar_snd_info *info = priv->info;
+
+       if (!of_data)
+               return;
+
+       info->flags = of_data->flags;
+}
+
 int rsnd_gen_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_gen *gen;
        int ret;
 
+       rsnd_of_parse_gen(pdev, of_data, priv);
+
        gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
        if (!gen) {
                dev_err(dev, "GEN allocate failed\n");
index c46e0af..619d198 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/io.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
 #include <linux/sh_dma.h>
 #include <linux/workqueue.h>
 #include <sound/rcar_snd.h>
@@ -113,6 +115,7 @@ enum rsnd_reg {
 #define RSND_REG_SRCOUT_TIMSEL4                RSND_REG_SHARE18
 #define RSND_REG_AUDIO_CLK_SEL2                RSND_REG_SHARE19
 
+struct rsnd_of_data;
 struct rsnd_priv;
 struct rsnd_mod;
 struct rsnd_dai;
@@ -260,6 +263,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
  *     R-Car Gen1/Gen2
  */
 int rsnd_gen_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
                   struct rsnd_priv *priv);
 void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
                               struct rsnd_mod *mod,
@@ -273,6 +277,7 @@ void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
 int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
 int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
 int rsnd_adg_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
                   struct rsnd_priv *priv);
 int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
                                  struct rsnd_mod *mod,
@@ -290,6 +295,10 @@ int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
 /*
  *     R-Car sound priv
  */
+struct rsnd_of_data {
+       u32 flags;
+};
+
 struct rsnd_priv {
 
        struct device *dev;
@@ -348,6 +357,7 @@ struct rsnd_priv {
  *     R-Car SRC
  */
 int rsnd_src_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
                   struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
 unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
@@ -366,6 +376,7 @@ int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
  *     R-Car SSI
  */
 int rsnd_ssi_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
                   struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
index ea6a214..eee75eb 100644 (file)
@@ -628,7 +628,41 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
        return &((struct rsnd_src *)(priv->src) + id)->mod;
 }
 
+static void rsnd_of_parse_src(struct platform_device *pdev,
+                             const struct rsnd_of_data *of_data,
+                             struct rsnd_priv *priv)
+{
+       struct device_node *src_node;
+       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+       struct rsnd_src_platform_info *src_info;
+       struct device *dev = &pdev->dev;
+       int nr;
+
+       if (!of_data)
+               return;
+
+       src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
+       if (!src_node)
+               return;
+
+       nr = of_get_child_count(src_node);
+       if (!nr)
+               return;
+
+       src_info = devm_kzalloc(dev,
+                               sizeof(struct rsnd_src_platform_info) * nr,
+                               GFP_KERNEL);
+       if (!src_info) {
+               dev_err(dev, "src info allocation error\n");
+               return;
+       }
+
+       info->src_info          = src_info;
+       info->src_info_nr       = nr;
+}
+
 int rsnd_src_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
                   struct rsnd_priv *priv)
 {
        struct rcar_snd_info *info = rsnd_priv_to_info(priv);
@@ -639,6 +673,8 @@ int rsnd_src_probe(struct platform_device *pdev,
        char name[RSND_SRC_NAME_SIZE];
        int i, nr;
 
+       rsnd_of_parse_src(pdev, of_data, priv);
+
        /*
         * init SRC
         */
index 633b23d..4b7e206 100644 (file)
@@ -588,7 +588,61 @@ static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *s
        }
 }
 
+
+static void rsnd_of_parse_ssi(struct platform_device *pdev,
+                             const struct rsnd_of_data *of_data,
+                             struct rsnd_priv *priv)
+{
+       struct device_node *node;
+       struct device_node *np;
+       struct rsnd_ssi_platform_info *ssi_info;
+       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+       struct device *dev = &pdev->dev;
+       int nr, i;
+
+       if (!of_data)
+               return;
+
+       node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
+       if (!node)
+               return;
+
+       nr = of_get_child_count(node);
+       if (!nr)
+               return;
+
+       ssi_info = devm_kzalloc(dev,
+                               sizeof(struct rsnd_ssi_platform_info) * nr,
+                               GFP_KERNEL);
+       if (!ssi_info) {
+               dev_err(dev, "ssi info allocation error\n");
+               return;
+       }
+
+       info->ssi_info          = ssi_info;
+       info->ssi_info_nr       = nr;
+
+       i = -1;
+       for_each_child_of_node(node, np) {
+               i++;
+
+               ssi_info = info->ssi_info + i;
+
+               /*
+                * pin settings
+                */
+               if (of_get_property(np, "shared-pin", NULL))
+                       ssi_info->flags |= RSND_SSI_CLK_PIN_SHARE;
+
+               /*
+                * irq
+                */
+               ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
+       }
+}
+
 int rsnd_ssi_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
                   struct rsnd_priv *priv)
 {
        struct rcar_snd_info *info = rsnd_priv_to_info(priv);
@@ -600,6 +654,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
        char name[RSND_SSI_NAME_SIZE];
        int i, nr;
 
+       rsnd_of_parse_ssi(pdev, of_data, priv);
+
        /*
         *      init SSI
         */