mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
ba9c949f79
commit
90e8e50fce
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue