mbox series

[v3,0/6] ASoC: Xilinx fixes

Message ID 20220120195832.1742271-1-robert.hancock@calian.com
Headers show
Series ASoC: Xilinx fixes | expand

Message

Robert Hancock Jan. 20, 2022, 7:58 p.m. UTC
There are drivers in mainline for the Xilinx Audio Formatter and Xilinx
I2S IP cores. However, because of a few issues, these were only really
usable with Xilinx's xlnx_pl_snd_card top-level driver, which is not in
mainline (and not suitable for mainline).

The fixes in this patchset, for the simple-card layer as well as the 
Xilinx drivers, now allow these drivers to be properly used with
simple-card without any out-of-tree support code.

Changes since v2:
-drop patches already merged
-added constraint to simple-card to allow enforcing valid sample rates

Changes since v1:
-formatting fixes
-renamed last_sysclk variables to sysclk
-require exact match for clock divisor rather than rounding to nearest
-broke out driver data structure change in xlnx_i2s to separate patch
-added constraints for sample rate based on sysclk to xlnx_i2s
-switched to separate function for DAI parsing for platforms in simple_card

Robert Hancock (6):
  ASoC: xilinx: xlnx_formatter_pcm: Handle sysclk setting
  ASoC: xilinx: xlnx_i2s: create drvdata structure
  ASoC: xilinx: xlnx_i2s: Handle sysclk setting
  ASoC: simple-card-utils: Set sysclk on all components
  ASoC: dt-bindings: simple-card: document new system-clock-fixed flag
  ASoC: simple-card-utils: Add new system-clock-fixed flag

 .../bindings/sound/simple-card.yaml           |  11 ++
 include/sound/simple_card_utils.h             |   1 +
 sound/soc/generic/simple-card-utils.c         |  86 ++++++++--
 sound/soc/xilinx/xlnx_formatter_pcm.c         |  25 +++
 sound/soc/xilinx/xlnx_i2s.c                   | 147 +++++++++++++-----
 5 files changed, 223 insertions(+), 47 deletions(-)

Comments

Amadeusz Sławiński Jan. 21, 2022, 9:06 a.m. UTC | #1
On 1/20/2022 8:58 PM, Robert Hancock wrote:
> An upcoming change will require storing additional driver data other
> than the memory base address. Create a drvdata structure and use that
> rather than storing the raw base address pointer.
> 
> Signed-off-by: Robert Hancock <robert.hancock@calian.com>
> ---
>   sound/soc/xilinx/xlnx_i2s.c | 66 ++++++++++++++++++++-----------------
>   1 file changed, 35 insertions(+), 31 deletions(-)
> 
> diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c
> index cc641e582c82..3bafa34b789a 100644
> --- a/sound/soc/xilinx/xlnx_i2s.c
> +++ b/sound/soc/xilinx/xlnx_i2s.c
> @@ -22,15 +22,20 @@
>   #define I2S_CH0_OFFSET			0x30
>   #define I2S_I2STIM_VALID_MASK		GENMASK(7, 0)
>   
> +struct xlnx_i2s_drv_data {
> +	struct snd_soc_dai_driver dai_drv;
> +	void __iomem *base;
> +};
> +
>   static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai *cpu_dai,
>   				    int div_id, int div)
>   {
> -	void __iomem *base = snd_soc_dai_get_drvdata(cpu_dai);
> +	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(cpu_dai);
>   
>   	if (!div || (div & ~I2S_I2STIM_VALID_MASK))
>   		return -EINVAL;
>   
> -	writel(div, base + I2S_I2STIM_OFFSET);
> +	writel(div, drv_data->base + I2S_I2STIM_OFFSET);
>   
>   	return 0;
>   }
> @@ -40,13 +45,13 @@ static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream,
>   			      struct snd_soc_dai *i2s_dai)
>   {
>   	u32 reg_off, chan_id;
> -	void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai);
> +	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(i2s_dai);
>   
>   	chan_id = params_channels(params) / 2;
>   
>   	while (chan_id > 0) {
>   		reg_off = I2S_CH0_OFFSET + ((chan_id - 1) * 4);
> -		writel(chan_id, base + reg_off);
> +		writel(chan_id, drv_data->base + reg_off);
>   		chan_id--;
>   	}
>   
> @@ -56,18 +61,18 @@ static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream,
>   static int xlnx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
>   			    struct snd_soc_dai *i2s_dai)
>   {
> -	void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai);
> +	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(i2s_dai);
>   
>   	switch (cmd) {
>   	case SNDRV_PCM_TRIGGER_START:
>   	case SNDRV_PCM_TRIGGER_RESUME:
>   	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> -		writel(1, base + I2S_CORE_CTRL_OFFSET);
> +		writel(1, drv_data->base + I2S_CORE_CTRL_OFFSET);
>   		break;
>   	case SNDRV_PCM_TRIGGER_STOP:
>   	case SNDRV_PCM_TRIGGER_SUSPEND:
>   	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> -		writel(0, base + I2S_CORE_CTRL_OFFSET);
> +		writel(0, drv_data->base + I2S_CORE_CTRL_OFFSET);
>   		break;
>   	default:
>   		return -EINVAL;
> @@ -95,20 +100,19 @@ MODULE_DEVICE_TABLE(of, xlnx_i2s_of_match);
>   
>   static int xlnx_i2s_probe(struct platform_device *pdev)
>   {
> -	void __iomem *base;
> -	struct snd_soc_dai_driver *dai_drv;
> +	struct xlnx_i2s_drv_data *drv_data;
>   	int ret;
>   	u32 ch, format, data_width;
>   	struct device *dev = &pdev->dev;
>   	struct device_node *node = dev->of_node;
>   
> -	dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL);
> -	if (!dai_drv)
> +	drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
> +	if (!drv_data)
>   		return -ENOMEM;
>   
> -	base = devm_platform_ioremap_resource(pdev, 0);
> -	if (IS_ERR(base))
> -		return PTR_ERR(base);
> +	drv_data->base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(drv_data->base))
> +		return PTR_ERR(drv_data->base);
>   
>   	ret = of_property_read_u32(node, "xlnx,num-channels", &ch);
>   	if (ret < 0) {
> @@ -134,35 +138,35 @@ static int xlnx_i2s_probe(struct platform_device *pdev)
>   	}
>   
>   	if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) {
> -		dai_drv->name = "xlnx_i2s_playback";
> -		dai_drv->playback.stream_name = "Playback";
> -		dai_drv->playback.formats = format;
> -		dai_drv->playback.channels_min = ch;
> -		dai_drv->playback.channels_max = ch;
> -		dai_drv->playback.rates	= SNDRV_PCM_RATE_8000_192000;
> -		dai_drv->ops = &xlnx_i2s_dai_ops;
> +		drv_data->dai_drv.name = "xlnx_i2s_playback";
> +		drv_data->dai_drv.playback.stream_name = "Playback";
> +		drv_data->dai_drv.playback.formats = format;
> +		drv_data->dai_drv.playback.channels_min = ch;
> +		drv_data->dai_drv.playback.channels_max = ch;
> +		drv_data->dai_drv.playback.rates	= SNDRV_PCM_RATE_8000_192000;
> +		drv_data->dai_drv.ops = &xlnx_i2s_dai_ops;
>   	} else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) {
> -		dai_drv->name = "xlnx_i2s_capture";
> -		dai_drv->capture.stream_name = "Capture";
> -		dai_drv->capture.formats = format;
> -		dai_drv->capture.channels_min = ch;
> -		dai_drv->capture.channels_max = ch;
> -		dai_drv->capture.rates = SNDRV_PCM_RATE_8000_192000;
> -		dai_drv->ops = &xlnx_i2s_dai_ops;
> +		drv_data->dai_drv.name = "xlnx_i2s_capture";
> +		drv_data->dai_drv.capture.stream_name = "Capture";
> +		drv_data->dai_drv.capture.formats = format;
> +		drv_data->dai_drv.capture.channels_min = ch;
> +		drv_data->dai_drv.capture.channels_max = ch;
> +		drv_data->dai_drv.capture.rates = SNDRV_PCM_RATE_8000_192000;
> +		drv_data->dai_drv.ops = &xlnx_i2s_dai_ops;
>   	} else {
>   		return -ENODEV;
>   	}
>   
> -	dev_set_drvdata(&pdev->dev, base);
> +	dev_set_drvdata(&pdev->dev, drv_data);
>   
>   	ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component,
> -					      dai_drv, 1);
> +					      &drv_data->dai_drv, 1);
>   	if (ret) {
>   		dev_err(&pdev->dev, "i2s component registration failed\n");
>   		return ret;
>   	}
>   
> -	dev_info(&pdev->dev, "%s DAI registered\n", dai_drv->name);
> +	dev_info(&pdev->dev, "%s DAI registered\n", drv_data->dai_drv.name);
>   
>   	return ret;
>   }

I don't think this patch is needed, snd_soc_dai, already has pointer to 
its snd_soc_dai_driver, so there is no need to keep it additionally in 
drvdata?

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/sound/soc-dai.h?h=v5.16#n431
Robert Hancock Jan. 21, 2022, 5:26 p.m. UTC | #2
On Fri, 2022-01-21 at 10:06 +0100, Amadeusz Sławiński wrote:
> On 1/20/2022 8:58 PM, Robert Hancock wrote:
> > An upcoming change will require storing additional driver data other
> > than the memory base address. Create a drvdata structure and use that
> > rather than storing the raw base address pointer.
> > 
> > Signed-off-by: Robert Hancock <robert.hancock@calian.com>
> > ---
> >   sound/soc/xilinx/xlnx_i2s.c | 66 ++++++++++++++++++++-----------------
> >   1 file changed, 35 insertions(+), 31 deletions(-)
> > 
> > diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c
> > index cc641e582c82..3bafa34b789a 100644
> > --- a/sound/soc/xilinx/xlnx_i2s.c
> > +++ b/sound/soc/xilinx/xlnx_i2s.c
> > @@ -22,15 +22,20 @@
> >   #define I2S_CH0_OFFSET			0x30
> >   #define I2S_I2STIM_VALID_MASK		GENMASK(7, 0)
> >   
> > +struct xlnx_i2s_drv_data {
> > +	struct snd_soc_dai_driver dai_drv;
> > +	void __iomem *base;
> > +};
> > +
> >   static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai *cpu_dai,
> >   				    int div_id, int div)
> >   {
> > -	void __iomem *base = snd_soc_dai_get_drvdata(cpu_dai);
> > +	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(cpu_dai);
> >   
> >   	if (!div || (div & ~I2S_I2STIM_VALID_MASK))
> >   		return -EINVAL;
> >   
> > -	writel(div, base + I2S_I2STIM_OFFSET);
> > +	writel(div, drv_data->base + I2S_I2STIM_OFFSET);
> >   
> >   	return 0;
> >   }
> > @@ -40,13 +45,13 @@ static int xlnx_i2s_hw_params(struct snd_pcm_substream
> > *substream,
> >   			      struct snd_soc_dai *i2s_dai)
> >   {
> >   	u32 reg_off, chan_id;
> > -	void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai);
> > +	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(i2s_dai);
> >   
> >   	chan_id = params_channels(params) / 2;
> >   
> >   	while (chan_id > 0) {
> >   		reg_off = I2S_CH0_OFFSET + ((chan_id - 1) * 4);
> > -		writel(chan_id, base + reg_off);
> > +		writel(chan_id, drv_data->base + reg_off);
> >   		chan_id--;
> >   	}
> >   
> > @@ -56,18 +61,18 @@ static int xlnx_i2s_hw_params(struct snd_pcm_substream
> > *substream,
> >   static int xlnx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
> >   			    struct snd_soc_dai *i2s_dai)
> >   {
> > -	void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai);
> > +	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(i2s_dai);
> >   
> >   	switch (cmd) {
> >   	case SNDRV_PCM_TRIGGER_START:
> >   	case SNDRV_PCM_TRIGGER_RESUME:
> >   	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> > -		writel(1, base + I2S_CORE_CTRL_OFFSET);
> > +		writel(1, drv_data->base + I2S_CORE_CTRL_OFFSET);
> >   		break;
> >   	case SNDRV_PCM_TRIGGER_STOP:
> >   	case SNDRV_PCM_TRIGGER_SUSPEND:
> >   	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> > -		writel(0, base + I2S_CORE_CTRL_OFFSET);
> > +		writel(0, drv_data->base + I2S_CORE_CTRL_OFFSET);
> >   		break;
> >   	default:
> >   		return -EINVAL;
> > @@ -95,20 +100,19 @@ MODULE_DEVICE_TABLE(of, xlnx_i2s_of_match);
> >   
> >   static int xlnx_i2s_probe(struct platform_device *pdev)
> >   {
> > -	void __iomem *base;
> > -	struct snd_soc_dai_driver *dai_drv;
> > +	struct xlnx_i2s_drv_data *drv_data;
> >   	int ret;
> >   	u32 ch, format, data_width;
> >   	struct device *dev = &pdev->dev;
> >   	struct device_node *node = dev->of_node;
> >   
> > -	dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL);
> > -	if (!dai_drv)
> > +	drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
> > +	if (!drv_data)
> >   		return -ENOMEM;
> >   
> > -	base = devm_platform_ioremap_resource(pdev, 0);
> > -	if (IS_ERR(base))
> > -		return PTR_ERR(base);
> > +	drv_data->base = devm_platform_ioremap_resource(pdev, 0);
> > +	if (IS_ERR(drv_data->base))
> > +		return PTR_ERR(drv_data->base);
> >   
> >   	ret = of_property_read_u32(node, "xlnx,num-channels", &ch);
> >   	if (ret < 0) {
> > @@ -134,35 +138,35 @@ static int xlnx_i2s_probe(struct platform_device
> > *pdev)
> >   	}
> >   
> >   	if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) {
> > -		dai_drv->name = "xlnx_i2s_playback";
> > -		dai_drv->playback.stream_name = "Playback";
> > -		dai_drv->playback.formats = format;
> > -		dai_drv->playback.channels_min = ch;
> > -		dai_drv->playback.channels_max = ch;
> > -		dai_drv->playback.rates	= SNDRV_PCM_RATE_8000_192000;
> > -		dai_drv->ops = &xlnx_i2s_dai_ops;
> > +		drv_data->dai_drv.name = "xlnx_i2s_playback";
> > +		drv_data->dai_drv.playback.stream_name = "Playback";
> > +		drv_data->dai_drv.playback.formats = format;
> > +		drv_data->dai_drv.playback.channels_min = ch;
> > +		drv_data->dai_drv.playback.channels_max = ch;
> > +		drv_data->dai_drv.playback.rates	=
> > SNDRV_PCM_RATE_8000_192000;
> > +		drv_data->dai_drv.ops = &xlnx_i2s_dai_ops;
> >   	} else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) {
> > -		dai_drv->name = "xlnx_i2s_capture";
> > -		dai_drv->capture.stream_name = "Capture";
> > -		dai_drv->capture.formats = format;
> > -		dai_drv->capture.channels_min = ch;
> > -		dai_drv->capture.channels_max = ch;
> > -		dai_drv->capture.rates = SNDRV_PCM_RATE_8000_192000;
> > -		dai_drv->ops = &xlnx_i2s_dai_ops;
> > +		drv_data->dai_drv.name = "xlnx_i2s_capture";
> > +		drv_data->dai_drv.capture.stream_name = "Capture";
> > +		drv_data->dai_drv.capture.formats = format;
> > +		drv_data->dai_drv.capture.channels_min = ch;
> > +		drv_data->dai_drv.capture.channels_max = ch;
> > +		drv_data->dai_drv.capture.rates = SNDRV_PCM_RATE_8000_192000;
> > +		drv_data->dai_drv.ops = &xlnx_i2s_dai_ops;
> >   	} else {
> >   		return -ENODEV;
> >   	}
> >   
> > -	dev_set_drvdata(&pdev->dev, base);
> > +	dev_set_drvdata(&pdev->dev, drv_data);
> >   
> >   	ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component,
> > -					      dai_drv, 1);
> > +					      &drv_data->dai_drv, 1);
> >   	if (ret) {
> >   		dev_err(&pdev->dev, "i2s component registration failed\n");
> >   		return ret;
> >   	}
> >   
> > -	dev_info(&pdev->dev, "%s DAI registered\n", dai_drv->name);
> > +	dev_info(&pdev->dev, "%s DAI registered\n", drv_data->dai_drv.name);
> >   
> >   	return ret;
> >   }
> 
> I don't think this patch is needed, snd_soc_dai, already has pointer to 
> its snd_soc_dai_driver, so there is no need to keep it additionally in 
> drvdata?
> 
> https://urldefense.com/v3/__https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/sound/soc-dai.h?h=v5.16*n431__;Iw!!IOGos0k!wK-VYGvndh29eBo3CZIPn7xG_X7ib4R8-hEVEyJc8aXkGoYORJ8cLH25u-K31eZAYe4$ 
> 
> 

It's not a pointer to the struct snd_soc_dai_driver that's in the drvdata
structure, snd_soc_dai_driver is actually part of the drvdata structure.
Previously it was allocating snd_soc_dai_driver by itself, and stuffing the
base address into the drvdata pointer. Now it's allocating one
xlnx_i2s_drv_data structure which contains both (and more attributes to come).
Mark Brown Jan. 25, 2022, 10:20 a.m. UTC | #3
On Thu, 20 Jan 2022 13:58:26 -0600, Robert Hancock wrote:
> There are drivers in mainline for the Xilinx Audio Formatter and Xilinx
> I2S IP cores. However, because of a few issues, these were only really
> usable with Xilinx's xlnx_pl_snd_card top-level driver, which is not in
> mainline (and not suitable for mainline).
> 
> The fixes in this patchset, for the simple-card layer as well as the
> Xilinx drivers, now allow these drivers to be properly used with
> simple-card without any out-of-tree support code.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next

Thanks!

[1/6] ASoC: xilinx: xlnx_formatter_pcm: Handle sysclk setting
      commit: 1c5091fbe7e0d0804158200b7feac5123f7b4fbd
[2/6] ASoC: xilinx: xlnx_i2s: create drvdata structure
      commit: 5e46c63ca22278fe363dfd9f5360c2e2ad082087
[3/6] ASoC: xilinx: xlnx_i2s: Handle sysclk setting
      commit: c47aef899c1bb0cbda48808356e7c040d95ca612
[4/6] ASoC: simple-card-utils: Set sysclk on all components
      commit: ce2f7b8d4290c22e462e465d1da38a1c113ae66a
[5/6] ASoC: dt-bindings: simple-card: document new system-clock-fixed flag
      commit: e9fed03aebacb8873dee8e2edfbce96f27f6c730
[6/6] ASoC: simple-card-utils: Add new system-clock-fixed flag
      commit: 5ca2ab4598179a2690a38420f3fde9f2ad79d55c

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark