| From 545e6a07d1d413829dc7a25c217eb657edefe2ea Mon Sep 17 00:00:00 2001 |
| From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Date: Mon, 17 Mar 2014 19:29:55 -0700 |
| Subject: ASoC: rsnd: add DeviceTree support |
| |
| 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 --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt |
| new file mode 100644 |
| index 000000000000..7c6d33f29796 |
| --- /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 953f1cce982d..69c44269ebdb 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 d836e8a9fdce..215b668166be 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 9094970dbdfb..50a1ef3eb1c6 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 c46e0afa54ae..619d198c7d2e 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 ea6a214985d0..eee75ebf961c 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 633b23d209b9..4b7e20603dd7 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 |
| */ |
| -- |
| 2.1.2 |
| |