[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <B01CBB94E8C4190E+aJ7M2v5yGGJw6S9T@LT-Guozexi>
Date: Fri, 15 Aug 2025 13:59:54 +0800
From: Troy Mitchell <troy.mitchell@...ux.spacemit.com>
To: Liam Girdwood <lgirdwood@...il.com>, Mark Brown <broonie@...nel.org>,
Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>, Yixun Lan <dlan@...too.org>,
Jaroslav Kysela <perex@...ex.cz>, Takashi Iwai <tiwai@...e.com>,
Philipp Zabel <p.zabel@...gutronix.de>
Cc: linux-sound@...r.kernel.org, devicetree@...r.kernel.org,
linux-riscv@...ts.infradead.org, spacemit@...ts.linux.dev,
linux-kernel@...r.kernel.org,
Troy Mitchell <troy.mitchell@...ux.spacemit.com>,
Jinmei Wei <weijinmei@...ux.spacemit.com>
Subject: Re: [PATCH 2/2] ASoC: spacemit: i2s: add support for K1 SoC
On Thu, Aug 14, 2025 at 04:54:20PM +0800, Troy Mitchell wrote:
> Add ASoC platform driver for the SpacemiT K1 SoC full-duplex I2S
> controller.
>
> Co-developer: Jinmei Wei <weijinmei@...ux.spacemit.com>
> Signed-off-by: Jinmei Wei <weijinmei@...ux.spacemit.com>
> Signed-off-by: Troy Mitchell <troy.mitchell@...ux.spacemit.com>
> ---
> sound/soc/Kconfig | 1 +
> sound/soc/Makefile | 1 +
> sound/soc/spacemit/Kconfig | 14 ++
> sound/soc/spacemit/Makefile | 5 +
> sound/soc/spacemit/k1_i2s.c | 444 ++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 465 insertions(+)
>
> diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
> index bf362bfca4564f0a7760850be8088ac7cc402b89..63d5c7a3ff4e1988cacfac01c58fd2bd36bd5903 100644
> --- a/sound/soc/Kconfig
> +++ b/sound/soc/Kconfig
> @@ -128,6 +128,7 @@ source "sound/soc/renesas/Kconfig"
> source "sound/soc/rockchip/Kconfig"
> source "sound/soc/samsung/Kconfig"
> source "sound/soc/sdca/Kconfig"
> +source "sound/soc/spacemit/Kconfig"
> source "sound/soc/spear/Kconfig"
> source "sound/soc/sprd/Kconfig"
> source "sound/soc/starfive/Kconfig"
> diff --git a/sound/soc/Makefile b/sound/soc/Makefile
> index 462322c38aa42d4c394736239de0317d5918d5a7..8c0480e6484e75eb0b6db306630ba77d259ba8e3 100644
> --- a/sound/soc/Makefile
> +++ b/sound/soc/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/
> obj-$(CONFIG_SND_SOC) += samsung/
> obj-$(CONFIG_SND_SOC) += sdca/
> obj-$(CONFIG_SND_SOC) += sof/
> +obj-$(CONFIG_SND_SOC) += spacemit/
> obj-$(CONFIG_SND_SOC) += spear/
> obj-$(CONFIG_SND_SOC) += sprd/
> obj-$(CONFIG_SND_SOC) += starfive/
> diff --git a/sound/soc/spacemit/Kconfig b/sound/soc/spacemit/Kconfig
> new file mode 100644
> index 0000000000000000000000000000000000000000..d0cb7400f9fb62c1178aac7049c59baaa5a2d4e4
> --- /dev/null
> +++ b/sound/soc/spacemit/Kconfig
> @@ -0,0 +1,14 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +menu "SpacemiT"
> + depends on COMPILE_TEST || ARCH_SPACEMIT
> + depends on HAVE_CLK
> +
> +config SND_SOC_K1_I2S
> + tristate "K1 I2S Device Driver"
> + select SND_SOC_GENERIC_DMAENGINE_PCM
I think I need select CMA and DMA_CMA here.
Otherwise I got this error:
```
[ 1.302864] ALSA pcmC0D0p,0:: cannot preallocate for size 32768
[ 1.308704] ALSA pcmC0D0c,0:d4026000.i2s0-ES8326 HiFi ES8326 HiFi-0: cannot preallocate for size 32768
```
- Troy
> + help
> + Say Y or M if you want to add support for I2S driver for
> + K1 I2S controller. The device supports up to maximum of
> + 2 channels each for play and record.
> +
> +endmenu
> diff --git a/sound/soc/spacemit/Makefile b/sound/soc/spacemit/Makefile
> new file mode 100644
> index 0000000000000000000000000000000000000000..9069de8ef89c84db8cc7d3a4d3b154fff9bd7aff
> --- /dev/null
> +++ b/sound/soc/spacemit/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# K1 Platform Support
> +snd-soc-k1-i2s-y := k1_i2s.o
> +
> +obj-$(CONFIG_SND_SOC_K1_I2S) += snd-soc-k1-i2s.o
> diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..9e41525afbf3f08ab9a26b562772861d86f39cd7
> --- /dev/null
> +++ b/sound/soc/spacemit/k1_i2s.c
> @@ -0,0 +1,444 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2025 Troy Mitchell <troy.mitchell@...ux.spacemit.com> */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/reset.h>
> +#include <sound/dmaengine_pcm.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +
> +#define SSCR 0x00 /* SPI/I2S top control register */
> +#define SSFCR 0x04 /* SPI/I2S FIFO control register */
> +#define SSINTEN 0x08 /* SPI/I2S interrupt enable register */
> +#define SSDATR 0x10 /* SPI/I2S data register */
> +#define SSPSP 0x18 /* SPI/I2S programmable serial protocol control register */
> +#define SSRWT 0x24 /* SPI/I2S root control register */
> +
> +/* SPI/I2S Work data size, register bits value 0~31 indicated data size 1~32 bits */
> +#define SSCR_FIELD_DSS GENMASK(9, 5)
> +#define SSCR_DW_8BYTE FIELD_PREP(SSCR_FIELD_DSS, 0x7)
> +#define SSCR_DW_16BYTE FIELD_PREP(SSCR_FIELD_DSS, 0xf)
> +#define SSCR_DW_18BYTE FIELD_PREP(SSCR_FIELD_DSS, 0x11)
> +#define SSCR_DW_32BYTE FIELD_PREP(SSCR_FIELD_DSS, 0x1f)
> +
> +#define SSCR_SSE BIT(0) /* SPI/I2S Enable */
> +#define SSCR_FRF_PSP GENMASK(2, 1) /* Frame Format*/
> +#define SSCR_TRAIL BIT(13) /* Trailing Byte */
> +
> +#define SSFCR_FIELD_TFT GENMASK(3, 0) /* TXFIFO Trigger Threshold */
> +#define SSFCR_FIELD_RFT GENMASK(8, 5) /* RXFIFO Trigger Threshold */
> +#define SSFCR_TSRE BIT(10) /* Transmit Service Request Enable */
> +#define SSFCR_RSRE BIT(11) /* Receive Service Request Enable */
> +
> +#define SSPSP_FSRT BIT(3) /* Frame Sync Relative Timing Bit */
> +#define SSPSP_SFRMP BIT(4) /* Serial Frame Polarity */
> +#define SSPSP_FIELD_SFRMWDTH GENMASK(17, 12) /* Serial Frame Width field */
> +
> +#define SSRWT_RWOT BIT(0) /* Receive Without Transmit */
> +
> +#define SPACEMIT_PCM_RATES SNDRV_PCM_RATE_8000_192000
> +#define SPACEMIT_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
> + SNDRV_PCM_FMTBIT_S16_LE | \
> + SNDRV_PCM_FMTBIT_S24_LE | \
> + SNDRV_PCM_FMTBIT_S32_LE)
> +
> +#define SPACEMIT_I2S_PERIOD_SIZE 1024
> +
> +struct spacemit_i2s_dev {
> + struct device *dev;
> +
> + void __iomem *base;
> +
> + struct reset_control *reset;
> +
> + struct clk *sysclk;
> + struct clk *bclk;
> + struct clk *sspa_clk;
> +
> + struct snd_dmaengine_dai_dma_data capture_dma_data;
> + struct snd_dmaengine_dai_dma_data playback_dma_data;
> +
> + bool has_capture;
> + bool has_playback;
> +
> + int dai_fmt;
> +
> + int started_count;
> +};
> +
> +static const struct snd_pcm_hardware spacemit_pcm_hardware = {
> + .info = SNDRV_PCM_INFO_INTERLEAVED |
> + SNDRV_PCM_INFO_BATCH,
> + .formats = SPACEMIT_PCM_FORMATS,
> + .rates = SPACEMIT_PCM_RATES,
> + .rate_min = SNDRV_PCM_RATE_8000,
> + .rate_max = SNDRV_PCM_RATE_192000,
> + .channels_min = 1,
> + .channels_max = 2,
> + .buffer_bytes_max = SPACEMIT_I2S_PERIOD_SIZE * 4 * 4,
> + .period_bytes_min = SPACEMIT_I2S_PERIOD_SIZE * 2,
> + .period_bytes_max = SPACEMIT_I2S_PERIOD_SIZE * 4,
> + .periods_min = 2,
> + .periods_max = 4,
> +};
> +
> +static const struct snd_dmaengine_pcm_config spacemit_dmaengine_pcm_config = {
> + .pcm_hardware = &spacemit_pcm_hardware,
> + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
> + .chan_names = {"tx", "rx"},
> + .prealloc_buffer_size = 32 * 1024,
> +};
> +
> +static void spacemit_i2s_init(struct spacemit_i2s_dev *i2s)
> +{
> + u32 sscr_val, sspsp_val, ssfcr_val, ssrwt_val;
> +
> + sscr_val = SSCR_TRAIL | SSCR_FRF_PSP;
> + ssfcr_val = FIELD_PREP(SSFCR_FIELD_TFT, 5) |
> + FIELD_PREP(SSFCR_FIELD_RFT, 5) |
> + SSFCR_RSRE | SSFCR_TSRE;
> + ssrwt_val = SSRWT_RWOT;
> +
> + /* SSPSP register was set by set_fmt */
> + sspsp_val = readl(i2s->base + SSPSP);
> + sspsp_val |= SSPSP_SFRMP;
> +
> + writel(sscr_val, i2s->base + SSCR);
> + writel(ssfcr_val, i2s->base + SSFCR);
> + writel(sspsp_val, i2s->base + SSPSP);
> + writel(ssrwt_val, i2s->base + SSRWT);
> + writel(0, i2s->base + SSINTEN);
> +}
> +
> +static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params,
> + struct snd_soc_dai *dai)
> +{
> + struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
> + struct snd_dmaengine_dai_dma_data *dma_data;
> + u32 data_width, data_bits;
> + unsigned long bclk_rate;
> + u32 val;
> + int ret;
> +
> + val = readl(i2s->base + SSCR);
> + if (val & SSCR_SSE)
> + return 0;
> +
> + dma_data = &i2s->playback_dma_data;
> +
> + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> + dma_data = &i2s->capture_dma_data;
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S8:
> + data_bits = 8;
> + data_width = SSCR_DW_8BYTE;
> + dma_data->maxburst = 8;
> + dma_data->addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> + break;
> + case SNDRV_PCM_FORMAT_S16_LE:
> + data_bits = 16;
> + data_width = SSCR_DW_16BYTE;
> + dma_data->maxburst = 16;
> + dma_data->addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> + if ((i2s->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) {
> + data_width = SSCR_DW_32BYTE;
> + dma_data->maxburst = 32;
> + dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> + }
> + break;
> + case SNDRV_PCM_FORMAT_S32_LE:
> + data_bits = 32;
> + data_width = SSCR_DW_32BYTE;
> + dma_data->maxburst = 32;
> + dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> + break;
> + default:
> + dev_dbg(i2s->dev, "unexpected data width type");
> + return -EINVAL;
> + }
> +
> + val = readl(i2s->base + SSCR);
> + val &= ~SSCR_DW_32BYTE;
> + val |= data_width;
> + writel(val, i2s->base + SSCR);
> +
> + bclk_rate = params_channels(params) *
> + params_rate(params) *
> + data_bits;
> +
> + ret = clk_set_rate(i2s->bclk, bclk_rate);
> + if (ret)
> + return ret;
> +
> + return clk_set_rate(i2s->sspa_clk, bclk_rate);
> +}
> +
> +static int spacemit_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
> + unsigned int freq, int dir)
> +{
> + struct spacemit_i2s_dev *i2s = dev_get_drvdata(cpu_dai->dev);
> +
> + if (freq == 0)
> + return 0;
> +
> + return clk_set_rate(i2s->sysclk, freq);
> +}
> +
> +static int spacemit_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
> + unsigned int fmt)
> +{
> + struct spacemit_i2s_dev *i2s = dev_get_drvdata(cpu_dai->dev);
> + u32 sspsp_val;
> +
> + sspsp_val = readl(i2s->base + SSPSP);
> + sspsp_val &= ~SSPSP_FIELD_SFRMWDTH;
> +
> + i2s->dai_fmt = fmt;
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + cpu_dai->driver->playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
> + cpu_dai->driver->capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
> + sspsp_val |= FIELD_PREP(SSPSP_FIELD_SFRMWDTH, 0x10) |
> + SSPSP_FSRT;
> + break;
> + case SND_SOC_DAIFMT_DSP_A:
> + case SND_SOC_DAIFMT_DSP_B:
> + cpu_dai->driver->playback.channels_min = 1;
> + cpu_dai->driver->playback.channels_max = 1;
> + cpu_dai->driver->capture.channels_min = 1;
> + cpu_dai->driver->capture.channels_max = 1;
> + cpu_dai->driver->playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
> + cpu_dai->driver->capture.formats = SNDRV_PCM_FMTBIT_S32_LE;
> + sspsp_val |= FIELD_PREP(SSPSP_FIELD_SFRMWDTH, 0x1);
> + break;
> + default:
> + dev_dbg(i2s->dev, "unexpected format type");
> + return -EINVAL;
> + }
> +
> + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A)
> + sspsp_val |= SSPSP_FSRT;
> +
> + writel(sspsp_val, i2s->base + SSPSP);
> +
> + return 0;
> +}
> +
> +static int spacemit_i2s_trigger(struct snd_pcm_substream *substream,
> + int cmd, struct snd_soc_dai *dai)
> +{
> + struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
> + u32 val;
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + if (!i2s->started_count) {
> + val = readl(i2s->base + SSCR);
> + val |= SSCR_SSE;
> + writel(val, i2s->base + SSCR);
> + }
> + i2s->started_count++;
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + if (i2s->started_count)
> + i2s->started_count--;
> +
> + if (!i2s->started_count) {
> + val = readl(i2s->base + SSCR);
> + val &= ~SSCR_SSE;
> + writel(val, i2s->base + SSCR);
> + }
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int spacemit_i2s_dai_probe(struct snd_soc_dai *dai)
> +{
> + struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
> +
> + snd_soc_dai_init_dma_data(dai,
> + i2s->has_playback ? &i2s->playback_dma_data : NULL,
> + i2s->has_capture ? &i2s->capture_dma_data : NULL);
> +
> + reset_control_deassert(i2s->reset);
> +
> + spacemit_i2s_init(i2s);
> +
> + return 0;
> +}
> +
> +static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = {
> + .probe = spacemit_i2s_dai_probe,
> + .hw_params = spacemit_i2s_hw_params,
> + .set_sysclk = spacemit_i2s_set_sysclk,
> + .set_fmt = spacemit_i2s_set_fmt,
> + .trigger = spacemit_i2s_trigger,
> +};
> +
> +static struct snd_soc_dai_driver spacemit_i2s_dai = {
> + .ops = &spacemit_i2s_dai_ops,
> + .playback = {
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SPACEMIT_PCM_RATES,
> + .rate_min = SNDRV_PCM_RATE_8000,
> + .rate_max = SNDRV_PCM_RATE_192000,
> + .formats = SPACEMIT_PCM_FORMATS,
> + },
> + .capture = {
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SPACEMIT_PCM_RATES,
> + .rate_min = SNDRV_PCM_RATE_8000,
> + .rate_max = SNDRV_PCM_RATE_192000,
> + .formats = SPACEMIT_PCM_FORMATS,
> + },
> + .symmetric_rate = 1,
> +};
> +
> +static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s,
> + struct snd_soc_dai_driver **dp,
> + dma_addr_t addr)
> +{
> + struct device_node *node = i2s->dev->of_node;
> + struct snd_soc_dai_driver *dai;
> + struct property *dma_names;
> + const char *dma_name;
> +
> + of_property_for_each_string(node, "dma-names", dma_names, dma_name) {
> + if (!strcmp(dma_name, "tx"))
> + i2s->has_playback = true;
> + if (!strcmp(dma_name, "rx"))
> + i2s->has_capture = true;
> + }
> +
> + dai = devm_kmemdup(i2s->dev, &spacemit_i2s_dai,
> + sizeof(*dai), GFP_KERNEL);
> + if (!dai)
> + return -ENOMEM;
> +
> + if (i2s->has_playback) {
> + dai->playback.stream_name = "Playback";
> + dai->playback.channels_min = 1;
> + dai->playback.channels_max = 2;
> + dai->playback.rates = SPACEMIT_PCM_RATES;
> + dai->playback.formats = SPACEMIT_PCM_FORMATS;
> +
> + i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> + i2s->playback_dma_data.maxburst = 32;
> + i2s->playback_dma_data.addr = addr;
> + }
> +
> + if (i2s->has_capture) {
> + dai->capture.stream_name = "Capture";
> + dai->capture.channels_min = 1;
> + dai->capture.channels_max = 2;
> + dai->capture.rates = SPACEMIT_PCM_RATES;
> + dai->capture.formats = SPACEMIT_PCM_FORMATS;
> +
> + i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> + i2s->capture_dma_data.maxburst = 32;
> + i2s->capture_dma_data.addr = addr;
> + }
> +
> + if (dp)
> + *dp = dai;
> +
> + return 0;
> +}
> +
> +static const struct snd_soc_component_driver spacemit_i2s_component = {
> + .name = "i2s-k1",
> + .legacy_dai_naming = 1,
> +};
> +
> +static int spacemit_i2s_probe(struct platform_device *pdev)
> +{
> + struct snd_soc_dai_driver *dai;
> + struct spacemit_i2s_dev *i2s;
> + struct resource *res;
> + struct clk *clk;
> + int ret;
> +
> + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
> + if (!i2s)
> + return -ENOMEM;
> +
> + i2s->dev = &pdev->dev;
> +
> + i2s->sysclk = devm_clk_get_enabled(i2s->dev, "sysclk");
> + if (IS_ERR(i2s->sysclk))
> + return dev_err_probe(i2s->dev, PTR_ERR(i2s->sysclk),
> + "failed to enable sysbase clock\n");
> +
> + i2s->bclk = devm_clk_get_enabled(i2s->dev, "bclk");
> + if (IS_ERR(i2s->bclk))
> + return dev_err_probe(i2s->dev, PTR_ERR(i2s->bclk), "failed to enable bit clock\n");
> +
> + clk = devm_clk_get_enabled(i2s->dev, "sspa_bus");
> + if (IS_ERR(clk))
> + return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable sspa_bus clock\n");
> +
> + i2s->sspa_clk = devm_clk_get_enabled(i2s->dev, "sspa");
> + if (IS_ERR(clk))
> + return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable sspa clock\n");
> +
> + i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
> + if (IS_ERR(i2s->base))
> + return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n");
> +
> + i2s->reset = devm_reset_control_get(&pdev->dev, NULL);
> + if (IS_ERR(i2s->reset))
> + return dev_err_probe(i2s->dev, PTR_ERR(i2s->reset),
> + "failed to get reset control");
> +
> + dev_set_drvdata(i2s->dev, i2s);
> +
> + spacemit_i2s_init_dai(i2s, &dai, res->start + SSDATR);
> +
> + ret = devm_snd_soc_register_component(i2s->dev,
> + &spacemit_i2s_component,
> + dai, 1);
> + if (ret)
> + return dev_err_probe(i2s->dev, ret, "failed to register component");
> +
> + return devm_snd_dmaengine_pcm_register(&pdev->dev, &spacemit_dmaengine_pcm_config, 0);
> +}
> +
> +static void spacemit_i2s_remove(struct platform_device *pdev)
> +{
> + struct spacemit_i2s_dev *i2s = dev_get_drvdata(&pdev->dev);
> +
> + reset_control_assert(i2s->reset);
> +}
> +
> +static const struct of_device_id spacemit_i2s_of_match[] = {
> + { .compatible = "spacemit,k1-i2s", },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, spacemit_i2s_of_match);
> +
> +static struct platform_driver spacemit_i2s_driver = {
> + .probe = spacemit_i2s_probe,
> + .remove = spacemit_i2s_remove,
> + .driver = {
> + .name = "i2s-k1",
> + .of_match_table = spacemit_i2s_of_match,
> + },
> +};
> +module_platform_driver(spacemit_i2s_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("I2S bus driver for SpacemiT K1 SoC");
>
> --
> 2.50.1
>
Powered by blists - more mailing lists