Subject: [Patch v2 3/4] ASoC: atmel-ssc-dai: register platform from DAIs

From: [email-masked] (Nicolas Ferre)
Date: Wed, 31 Oct 2012 10:41:03 +0100

On 10/31/2012 08:26 AM, Bo Shen :
> Register platform from DAIs
> 
> Add atmel-ssc-dai device tree support
> 
> Although atmel-ssc-dai is a virtual devices, but it needs the ssc
> controller as his parent. So, when use dai based on ssc, you should
> let the dai know which ssc should be used.
> Using ssc_request to implement this, so in dts file, need to assign
> "atmel,dai-master" to dai.
> 
> Signed-off-by: Bo Shen <[email-masked]>

Seems ok from an AT91 perspective.
I know little about audio + DT integration, so I am not judging this part.

Acked-by: Nicolas Ferre <[email-masked]>

> ---
>  .../devicetree/bindings/sound/atmel-ssc-dai.txt    |   18 ++
>  arch/arm/mach-at91/board-sam9g20ek.c               |    6 -
>  sound/soc/atmel/atmel-pcm.c                        |   23 +-
>  sound/soc/atmel/atmel-pcm.h                        |    3 +
>  sound/soc/atmel/atmel_ssc_dai.c                    |  251 ++++++--------------
>  sound/soc/atmel/sam9g20_wm8731.c                   |    2 +-
>  6 files changed, 103 insertions(+), 200 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/sound/atmel-ssc-dai.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/atmel-ssc-dai.txt b/Documentation/devicetree/bindings/sound/atmel-ssc-dai.txt
> new file mode 100644
> index 0000000..5afb0e9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/atmel-ssc-dai.txt
> @@ -0,0 +1,18 @@
> +* Atmel DAI interface based on SSC controller
> +
> +THe ssc is physical device. Which can be used to connect audio codec,
> +DAC, Magnetic card reader, and etc. So, build ssc controller as a
> +library.
> +
> +The dai is a virtual device, which will build on ssc controller.
> +So, use "atmel,dai-master" to let the dai know which ssc as his master.
> +
> +Required properties:
> +  - compatible: "atmel,atmel-ssc-dai"
> +  - atmel,dai-master: this dai base on which ssc controller
> +
> +Example:
> +dai: dai {
> +	compatible = "atmel,atmel-ssc-dai";
> +	atmel,dai-master = <&ssc0>;
> +};
> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
> index 5b6a6f9..ebdbf42 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -353,11 +353,6 @@ static struct i2c_board_info __initdata ek_i2c_devices[] = {
>          },
>  };
>  
> -static struct platform_device sam9g20ek_pcm_device = {
> -	.name	= "atmel-pcm-audio",
> -	.id = -1,
> -};
> -
>  static struct platform_device sam9g20ek_audio_device = {
>  	.name   = "at91sam9g20ek-audio",
>  	.id     = -1,
> @@ -365,7 +360,6 @@ static struct platform_device sam9g20ek_audio_device = {
>  
>  static void __init ek_add_device_audio(void)
>  {
> -	platform_device_register(&sam9g20ek_pcm_device);
>  	platform_device_register(&sam9g20ek_audio_device);
>  }
>  
> diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
> index 9b84f98..1e9cd2c 100644
> --- a/sound/soc/atmel/atmel-pcm.c
> +++ b/sound/soc/atmel/atmel-pcm.c
> @@ -473,28 +473,17 @@ static struct snd_soc_platform_driver atmel_soc_platform = {
>  	.resume		= atmel_pcm_resume,
>  };
>  
> -static int __devinit atmel_soc_platform_probe(struct platform_device *pdev)
> +int __devinit atmel_pcm_platform_register(struct device *dev)
>  {
> -	return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform);
> +	return snd_soc_register_platform(dev, &atmel_soc_platform);
>  }
> +EXPORT_SYMBOL(atmel_pcm_platform_register);
>  
> -static int __devexit atmel_soc_platform_remove(struct platform_device *pdev)
> +void __devexit atmel_pcm_platform_unregister(struct device *dev)
>  {
> -	snd_soc_unregister_platform(&pdev->dev);
> -	return 0;
> +	snd_soc_unregister_platform(dev);
>  }
> -
> -static struct platform_driver atmel_pcm_driver = {
> -	.driver = {
> -			.name = "atmel-pcm-audio",
> -			.owner = THIS_MODULE,
> -	},
> -
> -	.probe = atmel_soc_platform_probe,
> -	.remove = __devexit_p(atmel_soc_platform_remove),
> -};
> -
> -module_platform_driver(atmel_pcm_driver);
> +EXPORT_SYMBOL(atmel_pcm_platform_unregister);
>  
>  MODULE_AUTHOR("Sedji Gaouaou <[email-masked]>");
>  MODULE_DESCRIPTION("Atmel PCM module");
> diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
> index 5e0a95e..2d1c60f 100644
> --- a/sound/soc/atmel/atmel-pcm.h
> +++ b/sound/soc/atmel/atmel-pcm.h
> @@ -80,4 +80,7 @@ struct atmel_pcm_dma_params {
>  #define ssc_readx(base, reg)            (__raw_readl((base) + (reg)))
>  #define ssc_writex(base, reg, value)    __raw_writel((value), (base) + (reg))
>  
> +int __devexit atmel_pcm_platform_register(struct device *dev);
> +void __devexit atmel_pcm_platform_unregister(struct device *dev);
> +
>  #endif /* _ATMEL_PCM_H */
> diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
> index 354341e..5ff0774 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.c
> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> @@ -42,18 +42,13 @@
>  #include <sound/initval.h>
>  #include <sound/soc.h>
>  
> +#include <linux/of.h>
> +
>  #include <mach/hardware.h>
>  
>  #include "atmel-pcm.h"
>  #include "atmel_ssc_dai.h"
>  
> -
> -#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
> -#define NUM_SSC_DEVICES		1
> -#else
> -#define NUM_SSC_DEVICES		3
> -#endif
> -
>  /*
>   * SSC PDC registers required by the PCM DMA engine.
>   */
> @@ -96,63 +91,24 @@ static struct atmel_ssc_mask ssc_rx_mask = {
>  /*
>   * DMA parameters.
>   */
> -static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
> -	{{
> -	.name		= "SSC0 PCM out",
> -	.pdc		= &pdc_tx_reg,
> -	.mask		= &ssc_tx_mask,
> -	},
> +static struct atmel_pcm_dma_params ssc_dma_params[2] = {
>  	{
> -	.name		= "SSC0 PCM in",
> -	.pdc		= &pdc_rx_reg,
> -	.mask		= &ssc_rx_mask,
> -	} },
> -#if NUM_SSC_DEVICES == 3
> -	{{
> -	.name		= "SSC1 PCM out",
> +	.name		= "SSC PCM out",
>  	.pdc		= &pdc_tx_reg,
>  	.mask		= &ssc_tx_mask,
>  	},
>  	{
> -	.name		= "SSC1 PCM in",
> +	.name		= "SSC PCM in",
>  	.pdc		= &pdc_rx_reg,
>  	.mask		= &ssc_rx_mask,
> -	} },
> -	{{
> -	.name		= "SSC2 PCM out",
> -	.pdc		= &pdc_tx_reg,
> -	.mask		= &ssc_tx_mask,
>  	},
> -	{
> -	.name		= "SSC2 PCM in",
> -	.pdc		= &pdc_rx_reg,
> -	.mask		= &ssc_rx_mask,
> -	} },
> -#endif
>  };
>  
> -
> -static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
> -	{
> -	.name		= "ssc0",
> +static struct atmel_ssc_info ssc_info = {
> +	.name		= "ssc",
>  	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
>  	.dir_mask	= SSC_DIR_MASK_UNUSED,
>  	.initialized	= 0,
> -	},
> -#if NUM_SSC_DEVICES == 3
> -	{
> -	.name		= "ssc1",
> -	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
> -	.dir_mask	= SSC_DIR_MASK_UNUSED,
> -	.initialized	= 0,
> -	},
> -	{
> -	.name		= "ssc2",
> -	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
> -	.dir_mask	= SSC_DIR_MASK_UNUSED,
> -	.initialized	= 0,
> -	},
> -#endif
>  };
>  
>  
> @@ -205,7 +161,7 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
>  static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>  			     struct snd_soc_dai *dai)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	int dir_mask;
>  
>  	pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
> @@ -234,7 +190,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>  static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
>  			       struct snd_soc_dai *dai)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	struct atmel_pcm_dma_params *dma_params;
>  	int dir, dir_mask;
>  
> @@ -285,7 +241,7 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
>  static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
>  		unsigned int fmt)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  
>  	ssc_p->daifmt = fmt;
>  	return 0;
> @@ -297,7 +253,7 @@ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
>  static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
>  	int div_id, int div)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  
>  	switch (div_id) {
>  	case ATMEL_SSC_CMR_DIV:
> @@ -336,8 +292,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
>  	struct snd_soc_dai *dai)
>  {
>  	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
> -	int id = dai->id;
> -	struct atmel_ssc_info *ssc_p = &ssc_info[id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	struct atmel_pcm_dma_params *dma_params;
>  	int dir, channels, bits;
>  	u32 tfmr, rfmr, tcmr, rcmr;
> @@ -354,7 +309,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
>  	else
>  		dir = 1;
>  
> -	dma_params = &ssc_dma_params[id][dir];
> +	dma_params = &ssc_dma_params[dir];
>  	dma_params->ssc = ssc_p->ssc;
>  	dma_params->substream = substream;
>  
> @@ -603,7 +558,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
>  static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
>  			     struct snd_soc_dai *dai)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	struct atmel_pcm_dma_params *dma_params;
>  	int dir;
>  
> @@ -631,7 +586,7 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
>  	if (!cpu_dai->active)
>  		return 0;
>  
> -	ssc_p = &ssc_info[cpu_dai->id];
> +	ssc_p = &ssc_info;
>  
>  	/* Save the status register before disabling transmit and receive */
>  	ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
> @@ -660,7 +615,7 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
>  	if (!cpu_dai->active)
>  		return 0;
>  
> -	ssc_p = &ssc_info[cpu_dai->id];
> +	ssc_p = &ssc_info;
>  
>  	/* restore SSC register settings */
>  	ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
> @@ -689,31 +644,14 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
>  
>  static int atmel_ssc_probe(struct snd_soc_dai *dai)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	int ret = 0;
>  
>  	snd_soc_dai_set_drvdata(dai, ssc_p);
>  
> -	/*
> -	 * Request SSC device
> -	 */
> -	ssc_p->ssc = ssc_request(dai->id);
> -	if (IS_ERR(ssc_p->ssc)) {
> -		printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id);
> -		ret = PTR_ERR(ssc_p->ssc);
> -	}
> -
>  	return ret;
>  }
>  
> -static int atmel_ssc_remove(struct snd_soc_dai *dai)
> -{
> -	struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai);
> -
> -	ssc_free(ssc_p->ssc);
> -	return 0;
> -}
> -
>  #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
>  
>  #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
> @@ -728,11 +666,9 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
>  	.set_clkdiv	= atmel_ssc_set_dai_clkdiv,
>  };
>  
> -static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
> -	{
> -		.name = "atmel-ssc-dai.0",
> +static struct snd_soc_dai_driver atmel_ssc_dai = {
> +		.name = "atmel-ssc-dai",
>  		.probe = atmel_ssc_probe,
> -		.remove = atmel_ssc_remove,
>  		.suspend = atmel_ssc_suspend,
>  		.resume = atmel_ssc_resume,
>  		.playback = {
> @@ -746,119 +682,82 @@ static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
>  			.rates = ATMEL_SSC_RATES,
>  			.formats = ATMEL_SSC_FORMATS,},
>  		.ops = &atmel_ssc_dai_ops,
> -	},
> -#if NUM_SSC_DEVICES == 3
> -	{
> -		.name = "atmel-ssc-dai.1",
> -		.probe = atmel_ssc_probe,
> -		.remove = atmel_ssc_remove,
> -		.suspend = atmel_ssc_suspend,
> -		.resume = atmel_ssc_resume,
> -		.playback = {
> -			.channels_min = 1,
> -			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> -			.formats = ATMEL_SSC_FORMATS,},
> -		.capture = {
> -			.channels_min = 1,
> -			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> -			.formats = ATMEL_SSC_FORMATS,},
> -		.ops = &atmel_ssc_dai_ops,
> -	},
> -	{
> -		.name = "atmel-ssc-dai.2",
> -		.probe = atmel_ssc_probe,
> -		.remove = atmel_ssc_remove,
> -		.suspend = atmel_ssc_suspend,
> -		.resume = atmel_ssc_resume,
> -		.playback = {
> -			.channels_min = 1,
> -			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> -			.formats = ATMEL_SSC_FORMATS,},
> -		.capture = {
> -			.channels_min = 1,
> -			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> -			.formats = ATMEL_SSC_FORMATS,},
> -		.ops = &atmel_ssc_dai_ops,
> -	},
> -#endif
>  };
>  
>  static __devinit int asoc_ssc_probe(struct platform_device *pdev)
>  {
> -	BUG_ON(pdev->id < 0);
> -	BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai));
> -	return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]);
> +	struct ssc_device *ssc;
> +	int ret, id;
> +
> +	if (pdev->dev.of_node) {
> +		struct device_node *np = pdev->dev.of_node;
> +		struct device_node *dai_master_np;
> +
> +		dai_master_np = of_parse_phandle(np, "atmel,dai-master", 0);
> +		if (!dai_master_np) {
> +			dev_err(&pdev->dev, "No SSC for atmel dai");
> +			return -EINVAL;
> +		}
> +
> +		id = of_alias_get_id(dai_master_np, "ssc");
> +	} else {
> +		id = to_platform_device(pdev->dev.parent)->id;
> +	}
> +
> +	ssc = ssc_request(id);
> +	if (IS_ERR(ssc)) {
> +		dev_err(&pdev->dev, "Failed to request SSC %d\n", id);
> +		return PTR_ERR(ssc);
> +	}
> +	ssc_info.ssc = ssc;
> +	pdev->dev.parent = &(ssc->pdev->dev);
> +
> +	ret = snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
> +		goto err_unregister_dai;
> +	}
> +
> +	ret = atmel_pcm_platform_register(&pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
> +		goto err;
> +	};
> +
> +	return 0;
> +
> +err_unregister_dai:
> +	snd_soc_unregister_dai(&pdev->dev);
> +err:
> +	return ret;
>  }
>  
>  static int __devexit asoc_ssc_remove(struct platform_device *pdev)
>  {
> +	atmel_pcm_platform_unregister(&pdev->dev);
>  	snd_soc_unregister_dai(&pdev->dev);
>  	return 0;
>  }
>  
> +#ifdef CONFIG_OF
> +static const struct of_device_id atmel_ssc_dai_dt_ids[] = {
> +	{ .compatible = "atmel,atmel-ssc-dai", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, atmel_ssc_dai_dt_ids);
> +#endif
> +
>  static struct platform_driver asoc_ssc_driver = {
>  	.driver = {
> -			.name = "atmel-ssc-dai",
> -			.owner = THIS_MODULE,
> +		.name		= "atmel-ssc-dai",
> +		.owner		= THIS_MODULE,
> +		.of_match_table	= of_match_ptr(atmel_ssc_dai_dt_ids),
>  	},
>  
>  	.probe = asoc_ssc_probe,
>  	.remove = __devexit_p(asoc_ssc_remove),
>  };
>  
> -/**
> - * atmel_ssc_set_audio - Allocate the specified SSC for audio use.
> - */
> -int atmel_ssc_set_audio(int ssc_id)
> -{
> -	struct ssc_device *ssc;
> -	static struct platform_device *dma_pdev;
> -	struct platform_device *ssc_pdev;
> -	int ret;
> -
> -	if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai))
> -		return -EINVAL;
> -
> -	/* Allocate a dummy device for DMA if we don't have one already */
> -	if (!dma_pdev) {
> -		dma_pdev = platform_device_alloc("atmel-pcm-audio", -1);
> -		if (!dma_pdev)
> -			return -ENOMEM;
> -
> -		ret = platform_device_add(dma_pdev);
> -		if (ret < 0) {
> -			platform_device_put(dma_pdev);
> -			dma_pdev = NULL;
> -			return ret;
> -		}
> -	}
> -
> -	ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
> -	if (!ssc_pdev)
> -		return -ENOMEM;
> -
> -	/* If we can grab the SSC briefly to parent the DAI device off it */
> -	ssc = ssc_request(ssc_id);
> -	if (IS_ERR(ssc))
> -		pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n",
> -			PTR_ERR(ssc));
> -	else {
> -		ssc_pdev->dev.parent = &(ssc->pdev->dev);
> -		ssc_free(ssc);
> -	}
> -
> -	ret = platform_device_add(ssc_pdev);
> -	if (ret < 0)
> -		platform_device_put(ssc_pdev);
> -
> -	return ret;
> -}
> -EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
> -
>  module_platform_driver(asoc_ssc_driver);
>  
>  /* Module information */
> diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
> index e5e27db..c3e1df5 100644
> --- a/sound/soc/atmel/sam9g20_wm8731.c
> +++ b/sound/soc/atmel/sam9g20_wm8731.c
> @@ -182,7 +182,7 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
>  	.cpu_dai_name = "atmel-ssc-dai.0",
>  	.codec_dai_name = "wm8731-hifi",
>  	.init = at91sam9g20ek_wm8731_init,
> -	.platform_name = "atmel-pcm-audio",
> +	.platform_name = "atmel-ssc-dai.0",
>  	.codec_name = "wm8731.0-001b",
>  	.ops = &at91sam9g20ek_ops,
>  };
> 

-- 
Nicolas Ferre