Unverified Commit e49bcf4f authored by Mark Brown's avatar Mark Brown
Browse files

Merge series "Add audio driver base on rpmsg on i.MX platform" from Shengjiu...

Merge series "Add audio driver base on rpmsg on i.MX platform" from Shengjiu Wang <shengjiu.wang@nxp.com>:

On Asymmetric multiprocessor, there is Cortex-A core and Cortex-M core,
Linux is running on A core, RTOS is running on M core.
The audio hardware device can be controlled by Cortex-M device,
So audio playback/capture can be handled by M core.

Rpmsg is the interface for sending and receiving msg to and from M
core, that we can create a virtual sound on Cortex-A core side.

A core will tell the Cortex-M core sound format/rate/channel,
where is the data buffer, what is the period size, when to start,
when to stop and when suspend or resume happen, each of this behavior
there is defined rpmsg command.

Especially we designed the low power audio case, that is to
allocate a large buffer and fill the data, then Cortex-A core can go
to sleep mode, Cortex-M core continue to play the sound, when the
buffer is consumed, Cortex-M core will trigger the Cortex-A core to
wakeup to fill data.

changes in v5:
- remove unneeded property in binding doc and driver
- update binding doc according to Rob's comments.
- Fix link issue reported by kernel test robot

changes in v4:
- remove the sound card node, merge the property to cpu dai node
  according to Rob's comments.
- sound card device will be registered by cpu dai driver.
- Fix do_div issue reported by kernel test robot

changes in v3:
- add local refcount for clk enablement in hw_params()
- update the document according Rob's comments

changes in v2:
- update codes and comments according to Mark's comments

Shengjiu Wang (6):
  ASoC: soc-component: Add snd_soc_pcm_component_ack
  ASoC: fsl_rpmsg: Add CPU DAI driver for audio base on rpmsg
  ASoC: dt-bindings: fsl_rpmsg: Add binding doc for rpmsg audio device
  ASoC: imx-audio-rpmsg: Add rpmsg_driver for audio channel
  ASoC: imx-pcm-rpmsg: Add platform driver for audio base on rpmsg
  ASoC: imx-rpmsg: Add machine driver for audio base on rpmsg

 .../devicetree/bindings/sound/fsl,rpmsg.yaml  | 108 +++
 include/sound/soc-component.h                 |   3 +
 sound/soc/fsl/Kconfig                         |  30 +
 sound/soc/fsl/Makefile                        |   6 +
 sound/soc/fsl/fsl_rpmsg.c                     | 279 ++++++
 sound/soc/fsl/fsl_rpmsg.h                     |  35 +
 sound/soc/fsl/imx-audio-rpmsg.c               | 140 +++
 sound/soc/fsl/imx-pcm-rpmsg.c                 | 918 ++++++++++++++++++
 sound/soc/fsl/imx-pcm-rpmsg.h                 | 512 ++++++++++
 sound/soc/fsl/imx-rpmsg.c                     | 150 +++
 sound/soc/soc-component.c                     |  14 +
 sound/soc/soc-pcm.c                           |   2 +
 12 files changed, 2197 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
 create mode 100644 sound/soc/fsl/fsl_rpmsg.c
 create mode 100644 sound/soc/fsl/fsl_rpmsg.h
 create mode 100644 sound/soc/fsl/imx-audio-rpmsg.c
 create mode 100644 sound/soc/fsl/imx-pcm-rpmsg.c
 create mode 100644 sound/soc/fsl/imx-pcm-rpmsg.h
 create mode 100644 sound/soc/fsl/imx-rpmsg.c

--
2.27.0
parents b9af3fb7 39f8405c
Loading
Loading
Loading
Loading
+108 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/fsl,rpmsg.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: NXP Audio RPMSG CPU DAI Controller

maintainers:
  - Shengjiu Wang <shengjiu.wang@nxp.com>

description: |
  fsl_rpmsg is a virtual audio device. Mapping to real hardware devices
  are SAI, DMA controlled by Cortex M core. What we see from Linux
  side is a device which provides audio service by rpmsg channel.

properties:
  compatible:
    enum:
      - fsl,imx7ulp-rpmsg-audio
      - fsl,imx8mn-rpmsg-audio
      - fsl,imx8mm-rpmsg-audio
      - fsl,imx8mp-rpmsg-audio

  model:
    $ref: /schemas/types.yaml#/definitions/string
    description: User specified audio sound card name

  clocks:
    items:
      - description: Peripheral clock for register access
      - description: Master clock
      - description: DMA clock for DMA register access
      - description: Parent clock for multiple of 8kHz sample rates
      - description: Parent clock for multiple of 11kHz sample rates

  clock-names:
    items:
      - const: ipg
      - const: mclk
      - const: dma
      - const: pll8k
      - const: pll11k

  power-domains:
    description:
      List of phandle and PM domain specifier as documented in
      Documentation/devicetree/bindings/power/power_domain.txt
    maxItems: 1

  memory-region:
    $ref: /schemas/types.yaml#/definitions/phandle
    description:
      phandle to a node describing reserved memory (System RAM memory)
      The M core can't access all the DDR memory space on some platform,
      So reserved a specific memory for dma buffer which M core can
      access.
      (see bindings/reserved-memory/reserved-memory.txt)

  audio-codec:
    $ref: /schemas/types.yaml#/definitions/phandle
    description: The phandle to a node of audio codec

  audio-routing:
    $ref: /schemas/types.yaml#/definitions/non-unique-string-array
    description: |
      A list of the connections between audio components. Each entry is a
      pair of strings, the first being the connection's sink, the second
      being the connection's source.

  fsl,enable-lpa:
    $ref: /schemas/types.yaml#/definitions/flag
    description: enable low power audio path.

  fsl,rpmsg-out:
    $ref: /schemas/types.yaml#/definitions/flag
    description: |
      This is a boolean property. If present, the transmitting function
      will be enabled.

  fsl,rpmsg-in:
    $ref: /schemas/types.yaml#/definitions/flag
    description: |
      This is a boolean property. If present, the receiving function
      will be enabled.

required:
  - compatible
  - model

additionalProperties: false

examples:
  - |
    #include <dt-bindings/clock/imx8mn-clock.h>

    rpmsg_audio: rpmsg_audio {
        compatible = "fsl,imx8mn-rpmsg-audio";
        model = "wm8524-audio";
        fsl,enable-lpa;
        fsl,rpmsg-out;
        clocks = <&clk IMX8MN_CLK_SAI3_IPG>,
                 <&clk IMX8MN_CLK_SAI3_ROOT>,
                 <&clk IMX8MN_CLK_SDMA3_ROOT>,
                 <&clk IMX8MN_AUDIO_PLL1_OUT>,
                 <&clk IMX8MN_AUDIO_PLL2_OUT>;
        clock-names = "ipg", "mclk", "dma", "pll8k", "pll11k";
    };
+3 −0
Original line number Diff line number Diff line
@@ -146,6 +146,8 @@ struct snd_soc_component_driver {
	int (*mmap)(struct snd_soc_component *component,
		    struct snd_pcm_substream *substream,
		    struct vm_area_struct *vma);
	int (*ack)(struct snd_soc_component *component,
		   struct snd_pcm_substream *substream);

	const struct snd_compress_ops *compress_ops;

@@ -498,5 +500,6 @@ int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd,
					 void *stream);
void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd,
					  void *stream, int rollback);
int snd_soc_pcm_component_ack(struct snd_pcm_substream *substream);

#endif /* __SOC_COMPONENT_H */
+30 −0
Original line number Diff line number Diff line
@@ -115,10 +115,29 @@ config SND_SOC_FSL_AUD2HTX
config SND_SOC_FSL_UTILS
	tristate

config SND_SOC_FSL_RPMSG
	tristate "NXP Audio Base On RPMSG support"
	depends on COMMON_CLK
	depends on RPMSG
	select SND_SOC_IMX_RPMSG if SND_IMX_SOC != n
	help
	  Say Y if you want to add rpmsg audio support for the Freescale CPUs.
	  This option is only useful for out-of-tree drivers since
	  in-tree drivers select it automatically.

config SND_SOC_IMX_PCM_DMA
	tristate
	select SND_SOC_GENERIC_DMAENGINE_PCM

config SND_SOC_IMX_AUDIO_RPMSG
	tristate
	depends on RPMSG

config SND_SOC_IMX_PCM_RPMSG
	tristate
	depends on SND_SOC_IMX_AUDIO_RPMSG
	select SND_SOC_GENERIC_DMAENGINE_PCM

config SND_SOC_IMX_AUDMUX
	tristate "Digital Audio Mux module support"
	help
@@ -320,6 +339,17 @@ config SND_SOC_IMX_HDMI
	  Say Y if you want to add support for SoC audio on an i.MX board with
	  IMX HDMI.

config SND_SOC_IMX_RPMSG
	tristate "SoC Audio support for i.MX boards with rpmsg"
	depends on RPMSG
	select SND_SOC_IMX_PCM_RPMSG
	select SND_SOC_IMX_AUDIO_RPMSG
	help
	  SoC Audio support for i.MX boards with rpmsg.
	  There should be rpmsg devices defined in other core (M core)
	  Say Y if you want to add support for SoC audio on an i.MX board with
	  a rpmsg devices.

endif # SND_IMX_SOC

endmenu
+6 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ snd-soc-fsl-mqs-objs := fsl_mqs.o
snd-soc-fsl-easrc-objs := fsl_easrc.o
snd-soc-fsl-xcvr-objs := fsl_xcvr.o
snd-soc-fsl-aud2htx-objs := fsl_aud2htx.o
snd-soc-fsl-rpmsg-objs := fsl_rpmsg.o

obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
@@ -42,6 +43,7 @@ obj-$(CONFIG_SND_SOC_FSL_EASRC) += snd-soc-fsl-easrc.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
obj-$(CONFIG_SND_SOC_FSL_XCVR) += snd-soc-fsl-xcvr.o
obj-$(CONFIG_SND_SOC_FSL_AUD2HTX) += snd-soc-fsl-aud2htx.o
obj-$(CONFIG_SND_SOC_FSL_RPMSG) += snd-soc-fsl-rpmsg.o

# MPC5200 Platform Support
obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
@@ -58,6 +60,8 @@ obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o

obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
obj-$(CONFIG_SND_SOC_IMX_AUDIO_RPMSG) += imx-audio-rpmsg.o
obj-$(CONFIG_SND_SOC_IMX_PCM_RPMSG) += imx-pcm-rpmsg.o

# i.MX Machine Support
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
@@ -66,6 +70,7 @@ snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-spdif-objs := imx-spdif.o
snd-soc-imx-audmix-objs := imx-audmix.o
snd-soc-imx-hdmi-objs := imx-hdmi.o
snd-soc-imx-rpmsg-objs := imx-rpmsg.o

obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
@@ -73,3 +78,4 @@ obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o
obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o
+279 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2018-2021 NXP

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/pm_runtime.h>
#include <linux/rpmsg.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>

#include "fsl_rpmsg.h"
#include "imx-pcm.h"

#define FSL_RPMSG_RATES        (SNDRV_PCM_RATE_8000 | \
				SNDRV_PCM_RATE_16000 | \
				SNDRV_PCM_RATE_48000)
#define FSL_RPMSG_FORMATS	SNDRV_PCM_FMTBIT_S16_LE

/* 192kHz/32bit/2ch/60s size is 0x574e00 */
#define LPA_LARGE_BUFFER_SIZE  (0x6000000)

static const unsigned int fsl_rpmsg_rates[] = {
	8000, 11025, 16000, 22050, 44100,
	32000, 48000, 96000, 88200, 176400, 192000,
	352800, 384000, 705600, 768000, 1411200, 2822400,
};

static const struct snd_pcm_hw_constraint_list fsl_rpmsg_rate_constraints = {
	.count = ARRAY_SIZE(fsl_rpmsg_rates),
	.list = fsl_rpmsg_rates,
};

static int fsl_rpmsg_hw_params(struct snd_pcm_substream *substream,
			       struct snd_pcm_hw_params *params,
			       struct snd_soc_dai *dai)
{
	struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);
	struct clk *p = rpmsg->mclk, *pll = 0, *npll = 0;
	u64 rate = params_rate(params);
	int ret = 0;

	/* Get current pll parent */
	while (p && rpmsg->pll8k && rpmsg->pll11k) {
		struct clk *pp = clk_get_parent(p);

		if (clk_is_match(pp, rpmsg->pll8k) ||
		    clk_is_match(pp, rpmsg->pll11k)) {
			pll = pp;
			break;
		}
		p = pp;
	}

	/* Switch to another pll parent if needed. */
	if (pll) {
		npll = (do_div(rate, 8000) ? rpmsg->pll11k : rpmsg->pll8k);
		if (!clk_is_match(pll, npll)) {
			ret = clk_set_parent(p, npll);
			if (ret < 0)
				dev_warn(dai->dev, "failed to set parent %s: %d\n",
					 __clk_get_name(npll), ret);
		}
	}

	if (!(rpmsg->mclk_streams & BIT(substream->stream))) {
		ret = clk_prepare_enable(rpmsg->mclk);
		if (ret) {
			dev_err(dai->dev, "failed to enable mclk: %d\n", ret);
			return ret;
		}

		rpmsg->mclk_streams |= BIT(substream->stream);
	}

	return ret;
}

static int fsl_rpmsg_hw_free(struct snd_pcm_substream *substream,
			     struct snd_soc_dai *dai)
{
	struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);

	if (rpmsg->mclk_streams & BIT(substream->stream)) {
		clk_disable_unprepare(rpmsg->mclk);
		rpmsg->mclk_streams &= ~BIT(substream->stream);
	}

	return 0;
}

static int fsl_rpmsg_startup(struct snd_pcm_substream *substream,
			     struct snd_soc_dai *cpu_dai)
{
	int ret;

	ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
					 SNDRV_PCM_HW_PARAM_RATE,
					 &fsl_rpmsg_rate_constraints);

	return ret;
}

static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = {
	.startup	= fsl_rpmsg_startup,
	.hw_params      = fsl_rpmsg_hw_params,
	.hw_free        = fsl_rpmsg_hw_free,
};

static struct snd_soc_dai_driver fsl_rpmsg_dai = {
	.playback = {
		.stream_name = "CPU-Playback",
		.channels_min = 2,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_KNOT,
		.formats = FSL_RPMSG_FORMATS,
	},
	.capture = {
		.stream_name = "CPU-Capture",
		.channels_min = 2,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_KNOT,
		.formats = FSL_RPMSG_FORMATS,
	},
	.symmetric_rate        = 1,
	.symmetric_channels    = 1,
	.symmetric_sample_bits = 1,
	.ops = &fsl_rpmsg_dai_ops,
};

static const struct snd_soc_component_driver fsl_component = {
	.name           = "fsl-rpmsg",
};

static const struct of_device_id fsl_rpmsg_ids[] = {
	{ .compatible = "fsl,imx7ulp-rpmsg-audio"},
	{ .compatible = "fsl,imx8mm-rpmsg-audio"},
	{ .compatible = "fsl,imx8mn-rpmsg-audio"},
	{ .compatible = "fsl,imx8mp-rpmsg-audio"},
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);

static int fsl_rpmsg_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct fsl_rpmsg *rpmsg;
	int ret;

	rpmsg = devm_kzalloc(&pdev->dev, sizeof(struct fsl_rpmsg), GFP_KERNEL);
	if (!rpmsg)
		return -ENOMEM;

	if (of_property_read_bool(np, "fsl,enable-lpa")) {
		rpmsg->enable_lpa = 1;
		rpmsg->buffer_size = LPA_LARGE_BUFFER_SIZE;
	} else {
		rpmsg->buffer_size = IMX_DEFAULT_DMABUF_SIZE;
	}

	/* Get the optional clocks */
	rpmsg->ipg = devm_clk_get(&pdev->dev, "ipg");
	if (IS_ERR(rpmsg->ipg))
		rpmsg->ipg = NULL;

	rpmsg->mclk = devm_clk_get(&pdev->dev, "mclk");
	if (IS_ERR(rpmsg->mclk))
		rpmsg->mclk = NULL;

	rpmsg->dma = devm_clk_get(&pdev->dev, "dma");
	if (IS_ERR(rpmsg->dma))
		rpmsg->dma = NULL;

	rpmsg->pll8k = devm_clk_get(&pdev->dev, "pll8k");
	if (IS_ERR(rpmsg->pll8k))
		rpmsg->pll8k = NULL;

	rpmsg->pll11k = devm_clk_get(&pdev->dev, "pll11k");
	if (IS_ERR(rpmsg->pll11k))
		rpmsg->pll11k = NULL;

	platform_set_drvdata(pdev, rpmsg);
	pm_runtime_enable(&pdev->dev);

	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
					      &fsl_rpmsg_dai, 1);
	if (ret)
		return ret;

	rpmsg->card_pdev = platform_device_register_data(&pdev->dev,
							 "imx-audio-rpmsg",
							 PLATFORM_DEVID_NONE,
							 NULL,
							 0);
	if (IS_ERR(rpmsg->card_pdev)) {
		dev_err(&pdev->dev, "failed to register rpmsg card\n");
		ret = PTR_ERR(rpmsg->card_pdev);
		return ret;
	}

	return 0;
}

static int fsl_rpmsg_remove(struct platform_device *pdev)
{
	struct fsl_rpmsg *rpmsg = platform_get_drvdata(pdev);

	if (rpmsg->card_pdev)
		platform_device_unregister(rpmsg->card_pdev);

	return 0;
}

#ifdef CONFIG_PM
static int fsl_rpmsg_runtime_resume(struct device *dev)
{
	struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
	int ret;

	ret = clk_prepare_enable(rpmsg->ipg);
	if (ret) {
		dev_err(dev, "failed to enable ipg clock: %d\n", ret);
		goto ipg_err;
	}

	ret = clk_prepare_enable(rpmsg->dma);
	if (ret) {
		dev_err(dev, "Failed to enable dma clock %d\n", ret);
		goto dma_err;
	}

	return 0;

dma_err:
	clk_disable_unprepare(rpmsg->ipg);
ipg_err:
	return ret;
}

static int fsl_rpmsg_runtime_suspend(struct device *dev)
{
	struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);

	clk_disable_unprepare(rpmsg->dma);
	clk_disable_unprepare(rpmsg->ipg);

	return 0;
}
#endif

static const struct dev_pm_ops fsl_rpmsg_pm_ops = {
	SET_RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend,
			   fsl_rpmsg_runtime_resume,
			   NULL)
	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
				pm_runtime_force_resume)
};

static struct platform_driver fsl_rpmsg_driver = {
	.probe  = fsl_rpmsg_probe,
	.remove = fsl_rpmsg_remove,
	.driver = {
		.name = "fsl_rpmsg",
		.pm = &fsl_rpmsg_pm_ops,
		.of_match_table = fsl_rpmsg_ids,
	},
};
module_platform_driver(fsl_rpmsg_driver);

MODULE_DESCRIPTION("Freescale SoC Audio PRMSG CPU Interface");
MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
MODULE_ALIAS("platform:fsl_rpmsg");
MODULE_LICENSE("GPL");
Loading