diff mbox

[LTSI-3.14,078/894] ASoC: rsnd: add DeviceTree support

Message ID 1409209620-24487-79-git-send-email-horms+renesas@verge.net.au (mailing list archive)
State New, archived
Headers show

Commit Message

Simon Horman Aug. 28, 2014, 6:53 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>
Signed-off-by: Mark Brown <broonie@linaro.org>
(cherry picked from commit 90e8e50fce3585d6f9902701de08389b027dadc6)
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 .../devicetree/bindings/sound/renesas,rsnd.txt     |  96 ++++++++++++++++
 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/src.c                            |  36 ++++++
 sound/soc/sh/rcar/ssi.c                            |  56 ++++++++++
 7 files changed, 334 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..7c6d33f
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
@@ -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>;
+		};
+	};
+};
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 953f1cc..69c44269 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -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;
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index d836e8a..215b668 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
  */
@@ -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,
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 9094970..50a1ef3 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -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");
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index c46e0af..619d198 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>
@@ -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,
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index ea6a214..eee75eb 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -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
 	 */
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 633b23d..4b7e206 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
 	 */