diff mbox

[19/19] ASoC: rsnd: add DeviceTree support

Message ID 87lhwz7m3g.wl%kuninori.morimoto.gx@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Kuninori Morimoto Feb. 25, 2014, 6:17 a.m. UTC
From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

Support for loading the Renesas R-Car sound driver via DeviceTree.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 .../devicetree/bindings/sound/renesas,rsnd.txt     |  101 ++++++++++++++++
 sound/soc/sh/rcar/adg.c                            |    1 +
 sound/soc/sh/rcar/core.c                           |  122 +++++++++++++++++++-
 sound/soc/sh/rcar/gen.c                            |   15 +++
 sound/soc/sh/rcar/rsnd.h                           |   11 ++
 sound/soc/sh/rcar/scu.c                            |   36 ++++++
 sound/soc/sh/rcar/ssi.c                            |   56 +++++++++
 7 files changed, 339 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/renesas,rsnd.txt
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
new file mode 100644
index 0000000..47e3989
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
@@ -0,0 +1,101 @@ 
+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:
+- interrupt-parent		: Should contain SSI interrup parent
+- 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";
+	reg =	<0xffd90000 0x1000>, /* SRU */
+		<0xfffe0000 0x24>,   /* ADG */
+		<0xffd91000 0x1240>; /* SSI */
+
+	rcar_sound,scu {
+		scu0: scu@0 { };
+		scu1: scu@1 { };
+		scu2: scu@2 { };
+		scu3: scu@3 { };
+		scu4: scu@4 { };
+		scu5: scu@5 { };
+		scu6: scu@6 { };
+		scu7: scu@7 { };
+		scu8: scu@8 { };
+	};
+
+	rcar_sound,ssi {
+		ssi0: ssi@0 {
+			interrupt-parent = <&gic>;
+			interrupts = <0 101 0x4>;
+		};
+		ssi1: ssi@1 {
+			interrupt-parent = <&gic>;
+			interrupts = <0 101 0x4>;
+		};
+		ssi2: ssi@2 {
+			interrupt-parent = <&gic>;
+			interrupts = <0 101 0x4>;
+		};
+		ssi3: ssi@3 {
+			interrupt-parent = <&gic>;
+			interrupts = <0 101 0x4>;
+		};
+		ssi4: ssi@4 {
+			interrupt-parent = <&gic>;
+			interrupts = <0 101 0x4>;
+		};
+		ssi5: ssi@5 {
+			interrupt-parent = <&gic>;
+			interrupts = <0 102 0x4>;
+		};
+		ssi6: ssi@6 {
+			interrupt-parent = <&gic>;
+			interrupts = <0 102 0x4>;
+		};
+		ssi7: ssi@7 {
+			interrupt-parent = <&gic>;
+			interrupts = <0 102 0x4>;
+		};
+		ssi8: ssi@8 {
+			interrupt-parent = <&gic>;
+			interrupts = <0 102 0x4>;
+		};
+	};
+
+	rcar_sound,dai {
+		dai0 {
+			playback = <&ssi5 &scu5>;
+			capture  = <&ssi6 &scu6>;
+		};
+		dai1 {
+			playback = <&ssi3>;
+		};
+		dai2 {
+			capture  = <&ssi4>;
+		};
+		dai3 {
+			playback = <&ssi7>;
+		};
+		dai4 {
+			capture  = <&ssi8>;
+		};
+	};
+};
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 69d9394..5a897701 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -391,6 +391,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;
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 7ad63c7..9b648c3 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -100,6 +100,21 @@ 
 #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
  */
@@ -656,7 +671,92 @@  static int rsnd_path_exit(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 *scu_node,	*scu_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, scu_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");
+	scu_node = of_get_child_by_name(dev->of_node, "rcar_sound,scu");
+
+#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(scu);
+
+			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;
@@ -664,13 +764,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++) {
@@ -851,7 +954,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,
@@ -861,7 +967,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;
@@ -884,7 +999,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;
 	}
@@ -961,6 +1076,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,
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 4f2c1d0..8794711 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -360,13 +360,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");
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index ec6c58e..0e317e2 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -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>
@@ -89,6 +91,7 @@  enum rsnd_reg {
 	RSND_REG_MAX,
 };
 
+struct rsnd_of_data;
 struct rsnd_priv;
 struct rsnd_mod;
 struct rsnd_dai;
@@ -236,6 +239,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 rsnd_gen_remove(struct platform_device *pdev,
 		     struct rsnd_priv *priv);
@@ -251,6 +255,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);
 void rsnd_adg_remove(struct platform_device *pdev,
 		   struct rsnd_priv *priv);
@@ -270,6 +275,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;
@@ -328,6 +337,7 @@  struct rsnd_priv {
  *	R-Car SCU
  */
 int rsnd_scu_probe(struct platform_device *pdev,
+		   const struct rsnd_of_data *of_data,
 		   struct rsnd_priv *priv);
 void rsnd_scu_remove(struct platform_device *pdev,
 		     struct rsnd_priv *priv);
@@ -348,6 +358,7 @@  int rsnd_scu_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);
 void rsnd_ssi_remove(struct platform_device *pdev,
 		   struct rsnd_priv *priv);
diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c
index 40250ac..219ab27 100644
--- a/sound/soc/sh/rcar/scu.c
+++ b/sound/soc/sh/rcar/scu.c
@@ -628,7 +628,41 @@  struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id)
 	return &((struct rsnd_scu *)(priv->scu) + id)->mod;
 }
 
+static void rsnd_of_parse_scu(struct platform_device *pdev,
+			      const struct rsnd_of_data *of_data,
+			      struct rsnd_priv *priv)
+{
+	struct device_node *scu_node;
+	struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+	struct rsnd_scu_platform_info *scu_info;
+	struct device *dev = &pdev->dev;
+	int nr;
+
+	if (!of_data)
+		return;
+
+	scu_node = of_get_child_by_name(dev->of_node, "rcar_sound,scu");
+	if (!scu_node)
+		return;
+
+	nr = of_get_child_count(scu_node);
+	if (!nr)
+		return;
+
+	scu_info = devm_kzalloc(dev,
+				sizeof(struct rsnd_scu_platform_info) * nr,
+				GFP_KERNEL);
+	if (!scu_info) {
+		dev_err(dev, "scu info allocation error\n");
+		return;
+	}
+
+	info->scu_info		= scu_info;
+	info->scu_info_nr	= nr;
+}
+
 int rsnd_scu_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_scu_probe(struct platform_device *pdev,
 	char name[RSND_SCU_NAME_SIZE];
 	int i, nr;
 
+	rsnd_of_parse_scu(pdev, of_data, priv);
+
 	/*
 	 * init SCU
 	 */
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 0f3eeac..c9beef8 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -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
 	 */