new file mode 100644
@@ -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>;
+ };
+ };
+};
@@ -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;
@@ -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,
@@ -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");
@@ -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);
@@ -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
*/
@@ -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
*/