Allow a custom ASOC fabric driver with soc-of-simple. Register multiple DAIs. If no custom fabric driver, supply a default one so that ASOC binding will complete.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- include/sound/soc-of-simple.h | 16 +++- sound/last.c | 6 + sound/soc/codecs/tlv320aic26.c | 2 sound/soc/fsl/Kconfig | 2 sound/soc/fsl/mpc5200_psc_i2s.c | 4 - sound/soc/fsl/soc-of-simple.c | 160 ++++++++++++++++++++++++++++++++------- 6 files changed, 154 insertions(+), 36 deletions(-)
diff --git a/include/sound/soc-of-simple.h b/include/sound/soc-of-simple.h index a064e19..97d04af 100644 --- a/include/sound/soc-of-simple.h +++ b/include/sound/soc-of-simple.h @@ -12,13 +12,21 @@ #include <linux/of.h> #include <sound/soc.h>
+#define SOC_OF_SIMPLE_MAX_DAI 2 + int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, void *codec_data, struct snd_soc_dai *dai, - struct device_node *node); + int count, struct device_node *node); + +int of_snd_soc_register_cpu_dai(struct device_node *node, + struct snd_soc_dai *cpu_dai, int count); + +int of_snd_soc_register_platform(struct snd_soc_platform *platform); + +int of_snd_soc_register_fabric(const char *name, struct snd_soc_ops *ops, + int (*init)(struct snd_soc_codec *codec));
-int of_snd_soc_register_platform(struct snd_soc_platform *platform, - struct device_node *node, - struct snd_soc_dai *cpu_dai); +int of_snd_soc_register_default_fabric(void);
#endif
diff --git a/sound/last.c b/sound/last.c index bdd0857..b6efece 100644 --- a/sound/last.c +++ b/sound/last.c @@ -22,11 +22,15 @@ #define SNDRV_MAIN_OBJECT_FILE #include <linux/init.h> #include <sound/core.h> +#include <sound/soc-of-simple.h>
static int __init alsa_sound_last_init(void) { int idx, ok = 0; - + +#if defined(CONFIG_SND_SOC_OF_SIMPLE) + of_snd_soc_register_default_fabric(); +#endif printk(KERN_INFO "ALSA device list:\n"); for (idx = 0; idx < SNDRV_CARDS; idx++) if (snd_cards[idx] != NULL) { diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 3387d9e..95a7ac9 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -487,7 +487,7 @@ static int aic26_spi_probe(struct spi_device *spi)
#if defined(CONFIG_SND_SOC_OF_SIMPLE) /* Tell the of_soc helper about this codec */ - of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai, + of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai, 1, spi->dev.archdata.of_node); #endif
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index dc79bdf..8060b95 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,5 +1,5 @@ config SND_SOC_OF_SIMPLE - tristate + bool config SND_MPC52xx_DMA tristate diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 12a7917..5dbf1bc 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -353,8 +353,8 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, snd_soc_register_platform(&psc_dma_pcm_soc_platform);
/* Tell the ASoC OF helpers about it */ - of_snd_soc_register_platform(&psc_dma_pcm_soc_platform, op->node, - &psc_dma->dai); + of_snd_soc_register_platform(&mpc5200_audio_dma_platform); + of_snd_soc_register_cpu_dai(op->node, &psc_dma->dai, 1);
return 0; } diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c index 8bc5cd9..bf307aa 100644 --- a/sound/soc/fsl/soc-of-simple.c +++ b/sound/soc/fsl/soc-of-simple.c @@ -32,18 +32,23 @@ struct of_snd_soc_device { struct list_head list; struct snd_soc_device device; struct snd_soc_card card; - struct snd_soc_dai_link dai_link; + struct snd_soc_dai_link dai_link[SOC_OF_SIMPLE_MAX_DAI]; struct platform_device *pdev; - struct device_node *platform_node; + struct device_node *cpu_dai_node; struct device_node *codec_node; };
-static struct snd_soc_ops of_snd_soc_ops = { -}; +/* template values */ +struct snd_soc_platform *template_platform; +const char *template_name = NULL; +struct snd_soc_ops *template_ops; +int (*template_init)(struct snd_soc_codec *codec);
static struct of_snd_soc_device * of_snd_soc_get_device(struct device_node *codec_node) { + int i; + struct of_snd_soc_device *of_soc;
list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { @@ -58,10 +63,16 @@ of_snd_soc_get_device(struct device_node *codec_node) /* Initialize the structure and add it to the global list */ of_soc->codec_node = codec_node; of_soc->id = of_snd_soc_next_index++; - of_soc->card.dai_link = &of_soc->dai_link; - of_soc->card.num_links = 1; + of_soc->card.dai_link = of_soc->dai_link; of_soc->device.card = &of_soc->card; - of_soc->dai_link.ops = &of_snd_soc_ops; + of_soc->card.num_links = SOC_OF_SIMPLE_MAX_DAI; + for (i = 0; i < SOC_OF_SIMPLE_MAX_DAI; i++) { + of_soc->dai_link[i].ops = template_ops; + of_soc->dai_link[i].init = template_init; + } + of_soc->card.name = (char *)template_name; + of_soc->card.platform = template_platform; + list_add(&of_soc->list, &of_snd_soc_device_list);
return of_soc; @@ -74,10 +85,11 @@ static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
/* Only register the device if both the codec and platform have * been registered */ - if ((!of_soc->device.codec_data) || (!of_soc->platform_node)) + if ((!of_soc->device.codec_data) || (!of_soc->cpu_dai_node) || + !of_soc->card.platform || !of_soc->card.name) return;
- pr_info("platform<-->codec match achieved; registering machine\n"); + pr_info("platform<-->codec match achieved; registering fabric\n");
pdev = platform_device_alloc("soc-audio", of_soc->id); if (!pdev) { @@ -100,10 +112,10 @@ static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, void *codec_data, struct snd_soc_dai *dai, - struct device_node *node) + int count, struct device_node *node) { struct of_snd_soc_device *of_soc; - int rc = 0; + int i, rc = 0;
pr_info("registering ASoC codec driver: %s\n", node->full_name);
@@ -117,10 +129,11 @@ int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, /* Store the codec data */ of_soc->device.codec_data = codec_data; of_soc->device.codec_dev = codec_dev; - of_soc->dai_link.name = (char *)node->name; - of_soc->dai_link.stream_name = (char *)node->name; - of_soc->dai_link.codec_dai = dai; - + of_soc->card.num_links = min(count, of_soc->card.num_links); + for (i = 0; i < of_soc->card.num_links; i++) { + of_soc->dai_link[i].name = dai[i].name; + of_soc->dai_link[i].codec_dai = &dai[i]; + } /* Now try to register the SoC device */ of_snd_soc_register_device(of_soc);
@@ -130,21 +143,51 @@ int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, } EXPORT_SYMBOL_GPL(of_snd_soc_register_codec);
-int of_snd_soc_register_platform(struct snd_soc_platform *platform, - struct device_node *node, - struct snd_soc_dai *cpu_dai) +int of_snd_soc_register_cpu_dai(struct device_node *node, + struct snd_soc_dai *cpu_dai, int count) { struct of_snd_soc_device *of_soc; struct device_node *codec_node; const phandle *handle; - int len, rc = 0; + int i, len, rc = 0;
- pr_info("registering ASoC platform driver: %s\n", node->full_name); + pr_info("registering ASoC CPU DAI driver: %s\n", node->full_name);
handle = of_get_property(node, "codec-handle", &len); - if (!handle || len < sizeof(handle)) - return -ENODEV; - codec_node = of_find_node_by_phandle(*handle); + if (handle && len >= sizeof(handle)) + codec_node = of_find_node_by_phandle(*handle); + else { + /* Check for the codec child nodes */ + for_each_child_of_node(node, codec_node) { + struct platform_device *pdev; + struct dev_archdata dev_ad = {}; + char name[MODULE_NAME_LEN]; + const u32 *addr; + int len; + + if (of_modalias_node(codec_node, name, sizeof(name)) < 0) + continue; + + addr = of_get_property(codec_node, "reg", &len); + if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) { + pr_err("invalid codec reg in device tree\n"); + continue; + } + request_module("%s", name); + + pdev = platform_device_alloc(name, 0); + + dev_archdata_set_node(&dev_ad, codec_node); + pdev->dev.archdata = dev_ad; + + rc = platform_device_add(pdev); + if (rc) { + platform_device_put(pdev); + return rc; + } + break; + } + } if (!codec_node) return -ENODEV; pr_info("looking for codec: %s\n", codec_node->full_name); @@ -156,10 +199,12 @@ int of_snd_soc_register_platform(struct snd_soc_platform *platform, goto out; }
- of_soc->platform_node = node; - of_soc->dai_link.cpu_dai = cpu_dai; - of_soc->card.platform = platform; - of_soc->card.name = of_soc->dai_link.cpu_dai->name; + of_soc->cpu_dai_node = node; + of_soc->card.num_links = min(count, of_soc->card.num_links); + for (i = 0; i < of_soc->card.num_links; i++) { + of_soc->dai_link[i].stream_name = cpu_dai[i].name; + of_soc->dai_link[i].cpu_dai = &cpu_dai[i]; + }
/* Now try to register the SoC device */ of_snd_soc_register_device(of_soc); @@ -168,4 +213,65 @@ int of_snd_soc_register_platform(struct snd_soc_platform *platform, mutex_unlock(&of_snd_soc_mutex); return rc; } +EXPORT_SYMBOL_GPL(of_snd_soc_register_cpu_dai); + +int of_snd_soc_register_platform(struct snd_soc_platform *platform) +{ + struct of_snd_soc_device *of_soc; + int rc = 0; + + pr_info("registering ASoC platform driver: %s\n", platform->name); + template_platform = platform; + + mutex_lock(&of_snd_soc_mutex); + list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { + of_soc->card.platform = platform; + of_snd_soc_register_device(of_soc); + } + mutex_unlock(&of_snd_soc_mutex); + return rc; +} EXPORT_SYMBOL_GPL(of_snd_soc_register_platform); + +int of_snd_soc_register_fabric(const char *name, struct snd_soc_ops *ops, + int (*init)(struct snd_soc_codec *codec)) +{ + int i; + struct of_snd_soc_device *of_soc; + + pr_info("registering ASoC fabric driver: %s\n", name); + template_name = name; + template_ops = ops; + template_init = init; + + mutex_lock(&of_snd_soc_mutex); + list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { + for (i = 0; i < SOC_OF_SIMPLE_MAX_DAI; i++) { + of_soc->dai_link[i].ops = ops; + of_soc->dai_link[i].init = init; + } + of_soc->card.name = (char *)name; + of_snd_soc_register_device(of_soc); + } + mutex_unlock(&of_snd_soc_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(of_snd_soc_register_fabric); + +/* If no board specific fabric driver has been registered, register a default one */ +int of_snd_soc_register_default_fabric(void) +{ + struct device_node *root; + const char *model = ""; + + if (template_name != NULL) + return 0; + + root = of_find_node_by_path("/"); + if (root) + model = of_get_property(root, "model", NULL); + + return of_snd_soc_register_fabric(model, NULL, NULL); +} +EXPORT_SYMBOL_GPL(of_snd_soc_register_default_fabric); +