Support for loading the Renesas FSI driver via devicetree.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- .../devicetree/bindings/sound/renesas,fsi.txt | 23 ++++++ sound/soc/sh/fsi.c | 83 ++++++++++++++++++-- 2 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/renesas,fsi.txt
diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt new file mode 100644 index 0000000..503a7a3 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,fsi.txt @@ -0,0 +1,23 @@ +Renesas FSI + +Required properties: +- compatible : "renesas,sh_fsi2" or "renesas,sh_fsi" +- reg : Should contain the register physical address and length +- interrupts : Should contain FSI interrupt +- fsia,flags : FSI-A flags [spdif/cpg/stream] +- fsia,tx_id : FSI-A DMAEngine ID if needed +- fsib,flags : FSI-B flags [spdif/cpg/stream] +- fsib,tx_id : FSI-B DMAEngine ID if needed + +FSI needs simple-card driver. see also simple-card.txt + +Example: + +sh_fsi2: sh_fsi2@0xec230000 { + compatible = "renesas,sh_fsi2"; + reg = <0xec230000 0x400>; + interrupt-parent = <&gic>; + interrupts = <0 146 0x4>; + fsia,flags = "spdif", "cpg", "stream"; + fsia,tx_id = <23>; +}; diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index ef34ef8..7f30003 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -16,6 +16,8 @@ #include <linux/dma-mapping.h> #include <linux/pm_runtime.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/scatterlist.h> #include <linux/sh_dma.h> #include <linux/slab.h> @@ -297,7 +299,7 @@ struct fsi_master { int irq; struct fsi_priv fsia; struct fsi_priv fsib; - struct fsi_core *core; + const struct fsi_core *core; spinlock_t lock; };
@@ -1887,6 +1889,45 @@ static struct snd_soc_platform_driver fsi_soc_platform = { /* * platform function */ +static void fsi_of_parse(char *name, + struct device_node *np, + struct sh_fsi_port_info *info, + struct device *dev) +{ + int num, i, j, ret; + char prop[128]; + const char *str; + unsigned long flags = 0; + struct { + char *name; + unsigned int val; + } of_parse_table[] = { + { "spdif", SH_FSI_FMT_SPDIF }, + { "cpg", SH_FSI_CLK_CPG }, + { "stream", SH_FSI_ENABLE_STREAM_MODE }, + }; + + sprintf(prop, "%s,flags", name); + num = of_property_count_strings(np, prop); + for (i = 0; i < num; i++) { + ret = of_property_read_string_index(np, prop, i, &str); + if (ret < 0) + return; + + for (j = 0; j < ARRAY_SIZE(of_parse_table); j++) { + if (strcmp(str, of_parse_table[j].name) == 0) + flags |= of_parse_table[j].val; + } + } + info->flags = flags; + + sprintf(prop, "%s,%s", name, "tx_id"); + of_property_read_u32(np, prop, &info->tx_id); + + dev_dbg(dev, "%s flags : %lx\n", name, info->flags); + dev_dbg(dev, "%s tx_id : %d\n", name, info->tx_id); +} + static void fsi_port_info_init(struct fsi_priv *fsi, struct sh_fsi_port_info *info) { @@ -1914,22 +1955,40 @@ static void fsi_handler_init(struct fsi_priv *fsi, } }
+static struct of_device_id fsi_of_match[]; static int fsi_probe(struct platform_device *pdev) { struct fsi_master *master; - const struct platform_device_id *id_entry; + struct device_node *np = pdev->dev.of_node; struct sh_fsi_platform_info info; + const struct fsi_core *core; struct fsi_priv *fsi; struct resource *res; unsigned int irq; int ret;
memset(&info, 0, sizeof(info)); - if (pdev->dev.platform_data) - memcpy(&info, pdev->dev.platform_data, sizeof(info));
- id_entry = pdev->id_entry; - if (!id_entry) { + core = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(fsi_of_match, &pdev->dev); + if (of_id) { + core = of_id->data; + fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); + fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); + } + } else { + const struct platform_device_id *id_entry = pdev->id_entry; + if (id_entry) + core = (struct fsi_core *)id_entry->driver_data; + + if (pdev->dev.platform_data) + memcpy(&info, pdev->dev.platform_data, sizeof(info)); + } + + if (!core) { dev_err(&pdev->dev, "unknown fsi device\n"); return -ENODEV; } @@ -1956,7 +2015,7 @@ static int fsi_probe(struct platform_device *pdev)
/* master setting */ master->irq = irq; - master->core = (struct fsi_core *)id_entry->driver_data; + master->core = core; spin_lock_init(&master->lock);
/* FSI A setting */ @@ -1987,7 +2046,7 @@ static int fsi_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, master);
ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0, - id_entry->name, master); + dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "irq request err\n"); goto exit_fsib; @@ -2113,6 +2172,13 @@ static struct fsi_core fsi2_core = { .b_mclk = B_MST_CTLR, };
+static struct of_device_id fsi_of_match[] __devinitconst = { + { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, + { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, + {}, +}; +MODULE_DEVICE_TABLE(of, fsi_of_match); + static struct platform_device_id fsi_id_table[] = { { "sh_fsi", (kernel_ulong_t)&fsi1_core }, { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, @@ -2124,6 +2190,7 @@ static struct platform_driver fsi_driver = { .driver = { .name = "fsi-pcm-audio", .pm = &fsi_pm_ops, + .of_match_table = fsi_of_match, }, .probe = fsi_probe, .remove = fsi_remove,