From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752635AbcBEJ1s (ORCPT ); Fri, 5 Feb 2016 04:27:48 -0500 Received: from down.free-electrons.com ([37.187.137.238]:51090 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752324AbcBEJ1l (ORCPT ); Fri, 5 Feb 2016 04:27:41 -0500 Date: Fri, 5 Feb 2016 10:27:39 +0100 From: Maxime Ripard To: codekipper@gmail.com Cc: linux-arm-kernel@lists.infradead.org, linux-sunxi@googlegroups.com, lgirdwood@gmail.com, broonie@kernel.org, linux-kernel@vger.kernel.org, alsa-devel@alsa-project.org, be17068@iperbole.bo.it Subject: Re: [PATCH v4 2/2] ASoC: sunxi: Add support for the SPDIF block Message-ID: <20160205092739.GE1139@lukather> References: <1454584162-32449-1-git-send-email-codekipper@gmail.com> <1454584162-32449-3-git-send-email-codekipper@gmail.com> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="gDGSpKKIBgtShtf+" Content-Disposition: inline In-Reply-To: <1454584162-32449-3-git-send-email-codekipper@gmail.com> User-Agent: Mutt/1.5.23 (2014-03-12) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org --gDGSpKKIBgtShtf+ Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Hi, On Thu, Feb 04, 2016 at 12:09:22PM +0100, codekipper@gmail.com wrote: > From: Marcus Cooper >=20 > The sun4i, sun5i and sun7i SoC families have an SPDIF > block which is capable of playback and capture. >=20 > This patch enables the playback of this block for > the sun4i families. >=20 > Signed-off-by: Marcus Cooper > --- > sound/soc/sunxi/Kconfig | 8 + > sound/soc/sunxi/Makefile | 1 + > sound/soc/sunxi/sun4i-spdif.c | 561 ++++++++++++++++++++++++++++++++++++= ++++++ > 3 files changed, 570 insertions(+) > create mode 100644 sound/soc/sunxi/sun4i-spdif.c >=20 > diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig > index 84c72ec..6af7224 100644 > --- a/sound/soc/sunxi/Kconfig > +++ b/sound/soc/sunxi/Kconfig > @@ -8,4 +8,12 @@ config SND_SUN4I_CODEC > Select Y or M to add support for the Codec embedded in the Allwinner > A10 and affiliated SoCs. > =20 > +config SND_SUN4I_SPDIF > + tristate "Allwinner A10 SPDIF Support" > + depends on OF The indentation looks off. > + select SND_SOC_GENERIC_DMAENGINE_PCM > + select REGMAP_MMIO > + help > + Say Y or M to add support for the S/PDIF audio block in the Al= lwinner > + A10 and affiliated SoCs. Here as well. > endmenu > diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile > index ea8a08c..8f5e889 100644 > --- a/sound/soc/sunxi/Makefile > +++ b/sound/soc/sunxi/Makefile > @@ -1,2 +1,3 @@ > obj-$(CONFIG_SND_SUN4I_CODEC) +=3D sun4i-codec.o > =20 > +obj-$(CONFIG_SND_SUN4I_SPDIF) +=3D sun4i-spdif.o > diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c > new file mode 100644 > index 0000000..bb32344 > --- /dev/null > +++ b/sound/soc/sunxi/sun4i-spdif.c > @@ -0,0 +1,561 @@ > +/* > + * ALSA SoC SPDIF Audio Layer > + * > + * Copyright 2015 Andrea Venturi > + * Copyright 2015 Marcus Cooper > + * > + * Based on the Allwinner SDK driver, released under the GPL. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define SUN4I_SPDIF_CTL (0x00) > + #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */ > + #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2) > + #define SUN4I_SPDIF_CTL_GEN BIT(1) > + #define SUN4I_SPDIF_CTL_RESET BIT(0) > + > +#define SUN4I_SPDIF_TXCFG (0x04) > + #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31) > + #define SUN4I_SPDIF_TXCFG_ASS BIT(17) > + #define SUN4I_SPDIF_TXCFG_NONAUDIO BIT(16) > + #define SUN4I_SPDIF_TXCFG_TXRATIO(v) ((v) << 4) > + #define SUN4I_SPDIF_TXCFG_TXRATIO_MASK GENMASK(8, 4) > + #define SUN4I_SPDIF_TXCFG_FMTRVD GENMASK(3, 2) > + #define SUN4I_SPDIF_TXCFG_FMT16BIT (0 << 2) > + #define SUN4I_SPDIF_TXCFG_FMT20BIT (1 << 2) > + #define SUN4I_SPDIF_TXCFG_FMT24BIT (2 << 2) > + #define SUN4I_SPDIF_TXCFG_CHSTMODE BIT(1) > + #define SUN4I_SPDIF_TXCFG_TXEN BIT(0) > + > +#define SUN4I_SPDIF_RXCFG (0x08) > + #define SUN4I_SPDIF_RXCFG_LOCKFLAG BIT(4) > + #define SUN4I_SPDIF_RXCFG_CHSTSRC BIT(3) > + #define SUN4I_SPDIF_RXCFG_CHSTCP BIT(1) > + #define SUN4I_SPDIF_RXCFG_RXEN BIT(0) > + > +#define SUN4I_SPDIF_TXFIFO (0x0C) > + > +#define SUN4I_SPDIF_RXFIFO (0x10) > + > +#define SUN4I_SPDIF_FCTL (0x14) > + #define SUN4I_SPDIF_FCTL_FIFOSRC BIT(31) > + #define SUN4I_SPDIF_FCTL_FTX BIT(17) > + #define SUN4I_SPDIF_FCTL_FRX BIT(16) > + #define SUN4I_SPDIF_FCTL_TXTL(v) ((v) << 8) > + #define SUN4I_SPDIF_FCTL_TXTL_MASK GENMASK(12, 8) > + #define SUN4I_SPDIF_FCTL_RXTL(v) ((v) << 3) > + #define SUN4I_SPDIF_FCTL_RXTL_MASK GENMASK(7, 3) > + #define SUN4I_SPDIF_FCTL_TXIM BIT(2) > + #define SUN4I_SPDIF_FCTL_RXOM(v) ((v) << 0) > + #define SUN4I_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) > + > +#define SUN4I_SPDIF_FSTA (0x18) > + #define SUN4I_SPDIF_FSTA_TXE BIT(14) > + #define SUN4I_SPDIF_FSTA_TXECNTSHT (8) > + #define SUN4I_SPDIF_FSTA_RXA BIT(6) > + #define SUN4I_SPDIF_FSTA_RXACNTSHT (0) > + > +#define SUN4I_SPDIF_INT (0x1C) > + #define SUN4I_SPDIF_INT_RXLOCKEN BIT(18) > + #define SUN4I_SPDIF_INT_RXUNLOCKEN BIT(17) > + #define SUN4I_SPDIF_INT_RXPARERREN BIT(16) > + #define SUN4I_SPDIF_INT_TXDRQEN BIT(7) > + #define SUN4I_SPDIF_INT_TXUIEN BIT(6) > + #define SUN4I_SPDIF_INT_TXOIEN BIT(5) > + #define SUN4I_SPDIF_INT_TXEIEN BIT(4) > + #define SUN4I_SPDIF_INT_RXDRQEN BIT(2) > + #define SUN4I_SPDIF_INT_RXOIEN BIT(1) > + #define SUN4I_SPDIF_INT_RXAIEN BIT(0) > + > +#define SUN4I_SPDIF_ISTA (0x20) > + #define SUN4I_SPDIF_ISTA_RXLOCKSTA BIT(18) > + #define SUN4I_SPDIF_ISTA_RXUNLOCKSTA BIT(17) > + #define SUN4I_SPDIF_ISTA_RXPARERRSTA BIT(16) > + #define SUN4I_SPDIF_ISTA_TXUSTA BIT(6) > + #define SUN4I_SPDIF_ISTA_TXOSTA BIT(5) > + #define SUN4I_SPDIF_ISTA_TXESTA BIT(4) > + #define SUN4I_SPDIF_ISTA_RXOSTA BIT(1) > + #define SUN4I_SPDIF_ISTA_RXASTA BIT(0) > + > +#define SUN4I_SPDIF_TXCNT (0x24) > + > +#define SUN4I_SPDIF_RXCNT (0x28) > + > +#define SUN4I_SPDIF_TXCHSTA0 (0x2C) > + #define SUN4I_SPDIF_TXCHSTA0_CLK(v) ((v) << 28) > + #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ(v) ((v) << 24) > + #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ_MASK GENMASK(27, 24) > + #define SUN4I_SPDIF_TXCHSTA0_CHNUM(v) ((v) << 20) > + #define SUN4I_SPDIF_TXCHSTA0_CHNUM_MASK GENMASK(23, 20) > + #define SUN4I_SPDIF_TXCHSTA0_SRCNUM(v) ((v) << 16) > + #define SUN4I_SPDIF_TXCHSTA0_CATACOD(v) ((v) << 8) > + #define SUN4I_SPDIF_TXCHSTA0_MODE(v) ((v) << 6) > + #define SUN4I_SPDIF_TXCHSTA0_EMPHASIS(v) ((v) << 3) > + #define SUN4I_SPDIF_TXCHSTA0_CP BIT(2) > + #define SUN4I_SPDIF_TXCHSTA0_AUDIO BIT(1) > + #define SUN4I_SPDIF_TXCHSTA0_PRO BIT(0) > + > +#define SUN4I_SPDIF_TXCHSTA1 (0x30) > + #define SUN4I_SPDIF_TXCHSTA1_CGMSA(v) ((v) << 8) > + #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ(v) ((v) << 4) > + #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ_MASK GENMASK(7, 4) > + #define SUN4I_SPDIF_TXCHSTA1_SAMWORDLEN(v) ((v) << 1) > + #define SUN4I_SPDIF_TXCHSTA1_MAXWORDLEN BIT(0) > + > +#define SUN4I_SPDIF_RXCHSTA0 (0x34) > + #define SUN4I_SPDIF_RXCHSTA0_CLK(v) ((v) << 28) > + #define SUN4I_SPDIF_RXCHSTA0_SAMFREQ(v) ((v) << 24) > + #define SUN4I_SPDIF_RXCHSTA0_CHNUM(v) ((v) << 20) > + #define SUN4I_SPDIF_RXCHSTA0_SRCNUM(v) ((v) << 16) > + #define SUN4I_SPDIF_RXCHSTA0_CATACOD(v) ((v) << 8) > + #define SUN4I_SPDIF_RXCHSTA0_MODE(v) ((v) << 6) > + #define SUN4I_SPDIF_RXCHSTA0_EMPHASIS(v) ((v) << 3) > + #define SUN4I_SPDIF_RXCHSTA0_CP BIT(2) > + #define SUN4I_SPDIF_RXCHSTA0_AUDIO BIT(1) > + #define SUN4I_SPDIF_RXCHSTA0_PRO BIT(0) > + > +#define SUN4I_SPDIF_RXCHSTA1 (0x38) > + #define SUN4I_SPDIF_RXCHSTA1_CGMSA(v) ((v) << 8) > + #define SUN4I_SPDIF_RXCHSTA1_ORISAMFREQ(v) ((v) << 4) > + #define SUN4I_SPDIF_RXCHSTA1_SAMWORDLEN(v) ((v) << 1) > + #define SUN4I_SPDIF_RXCHSTA1_MAXWORDLEN BIT(0) > + > +/* Defines for Sampling Frequency */ > +#define SUN4I_SPDIF_SAMFREQ_44_1KHZ 0x0 > +#define SUN4I_SPDIF_SAMFREQ_NOT_INDICATED 0x1 > +#define SUN4I_SPDIF_SAMFREQ_48KHZ 0x2 > +#define SUN4I_SPDIF_SAMFREQ_32KHZ 0x3 > +#define SUN4I_SPDIF_SAMFREQ_22_05KHZ 0x4 > +#define SUN4I_SPDIF_SAMFREQ_24KHZ 0x6 > +#define SUN4I_SPDIF_SAMFREQ_88_2KHZ 0x8 > +#define SUN4I_SPDIF_SAMFREQ_76_8KHZ 0x9 > +#define SUN4I_SPDIF_SAMFREQ_96KHZ 0xa > +#define SUN4I_SPDIF_SAMFREQ_176_4KHZ 0xc > +#define SUN4I_SPDIF_SAMFREQ_192KHZ 0xe > + > +/* > + * Original sampling frequency can be represented by inverting the value= of the > + * sampling frequency. > + */ > +#define ORIGINAL(v) ((~v) & 0xf) You're not using that macro anywhere. > + > +struct sun4i_spdif_dev { > + struct platform_device *pdev; > + struct clk *spdif_clk; > + struct clk *apb_clk; > + struct snd_soc_dai_driver cpu_dai_drv; > + struct regmap *regmap; > + struct snd_dmaengine_dai_dma_data dma_params_tx; > +}; > + > +static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) > +{ > + /* soft reset SPDIF */ > + regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET); > + > + /* flush TX FIFO */ > + regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, > + SUN4I_SPDIF_FCTL_FTX, SUN4I_SPDIF_FCTL_FTX); > + > + /* clear TX counter */ > + regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0); > +} > + > +static void sun4i_snd_txctrl_on(struct snd_pcm_substream *substream, > + struct sun4i_spdif_dev *host) > +{ > + if (substream->runtime->channels =3D=3D 1) > + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, > + SUN4I_SPDIF_TXCFG_SINGLEMOD, > + SUN4I_SPDIF_TXCFG_SINGLEMOD); > + > + /* SPDIF TX ENABLE */ > + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, > + SUN4I_SPDIF_TXCFG_TXEN, SUN4I_SPDIF_TXCFG_TXEN); > + > + /* DRQ ENABLE */ > + regmap_update_bits(host->regmap, SUN4I_SPDIF_INT, > + SUN4I_SPDIF_INT_TXDRQEN, SUN4I_SPDIF_INT_TXDRQEN); > + > + /* Global enable */ > + regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL, > + SUN4I_SPDIF_CTL_GEN, SUN4I_SPDIF_CTL_GEN); > +} > + > +static void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream, > + struct sun4i_spdif_dev *host) > +{ > + /* SPDIF TX DISABLE */ > + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, > + SUN4I_SPDIF_TXCFG_TXEN, 0); > + > + /* DRQ DISABLE */ > + regmap_update_bits(host->regmap, SUN4I_SPDIF_INT, > + SUN4I_SPDIF_INT_TXDRQEN, 0); > + > + /* Global disable */ > + regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL, > + SUN4I_SPDIF_CTL_GEN, 0); > +} > + > +static int sun4i_spdif_startup(struct snd_pcm_substream *substream, > + struct snd_soc_dai *cpu_dai) > +{ > + struct snd_soc_pcm_runtime *rtd =3D substream->private_data; > + struct sun4i_spdif_dev *host =3D snd_soc_dai_get_drvdata(rtd->cpu_dai); > + > + if (substream->stream !=3D SNDRV_PCM_STREAM_PLAYBACK) > + return -EINVAL; > + > + sun4i_spdif_configure(host); > + > + return 0; > +} > + > +static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, > + struct snd_pcm_hw_params *params, > + struct snd_soc_dai *cpu_dai) > +{ > + int ret =3D 0; > + int fmt; > + unsigned long rate =3D params_rate(params); > + u32 mclk_div =3D 0; > + unsigned int mclk =3D 0; > + u32 reg_val; > + struct sun4i_spdif_dev *host =3D snd_soc_dai_get_drvdata(cpu_dai); > + struct platform_device *pdev =3D host->pdev; > + > + /* Add the PCM and raw data select interface */ > + switch (params_channels(params)) { > + case 1: /* PCM mode */ > + case 2: > + fmt =3D 0; > + break; > + case 4: /* raw data mode */ > + fmt =3D SUN4I_SPDIF_TXCFG_NONAUDIO; > + break; > + default: > + return -EINVAL; > + } > + > + switch (params_format(params)) { > + case SNDRV_PCM_FORMAT_S16_LE: > + fmt |=3D SUN4I_SPDIF_TXCFG_FMT16BIT; > + break; > + case SNDRV_PCM_FORMAT_S20_3LE: > + fmt |=3D SUN4I_SPDIF_TXCFG_FMT20BIT; > + break; > + case SNDRV_PCM_FORMAT_S24_LE: > + fmt |=3D SUN4I_SPDIF_TXCFG_FMT24BIT; > + break; > + default: > + return -EINVAL; > + } > + > + switch (rate) { > + case 22050: > + case 44100: > + case 88200: > + case 176400: > + mclk =3D 22579200; > + break; > + case 24000: > + case 32000: > + case 48000: > + case 96000: > + case 192000: > + mclk =3D 24576000; > + break; > + default: > + return -EINVAL; > + } > + > + ret =3D clk_set_rate(host->spdif_clk, mclk); > + if (ret < 0) { > + dev_err(&pdev->dev, > + "Setting SPDIF clock rate for %d Hz failed!\n", mclk); > + return ret; > + } > + > + regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, > + SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM); > + > + switch (rate) { > + case 22050: > + case 24000: > + mclk_div =3D 8; > + break; > + case 32000: > + mclk_div =3D 6; > + break; > + case 44100: > + case 48000: > + mclk_div =3D 4; > + break; > + case 88200: > + case 96000: > + mclk_div =3D 2; > + break; > + case 176400: > + case 192000: > + mclk_div =3D 1; > + break; > + default: > + return -EINVAL; > + } > + > + reg_val =3D 0; > + reg_val |=3D SUN4I_SPDIF_TXCFG_ASS; > + reg_val |=3D fmt; /* set non audio and bit depth */ > + reg_val |=3D SUN4I_SPDIF_TXCFG_CHSTMODE; > + reg_val |=3D SUN4I_SPDIF_TXCFG_TXRATIO(mclk_div - 1); > + regmap_write(host->regmap, SUN4I_SPDIF_TXCFG, reg_val); > + > + return 0; > +} > + > +static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int = cmd, > + struct snd_soc_dai *dai) > +{ > + int ret =3D 0; > + struct sun4i_spdif_dev *host =3D snd_soc_dai_get_drvdata(dai); > + > + if (substream->stream !=3D SNDRV_PCM_STREAM_PLAYBACK) > + return -EINVAL; > + > + switch (cmd) { > + case SNDRV_PCM_TRIGGER_START: > + case SNDRV_PCM_TRIGGER_RESUME: > + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: > + sun4i_snd_txctrl_on(substream, host); > + break; > + > + case SNDRV_PCM_TRIGGER_STOP: > + case SNDRV_PCM_TRIGGER_SUSPEND: > + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: > + sun4i_snd_txctrl_off(substream, host); > + break; > + > + default: > + ret =3D -EINVAL; > + break; > + } > + return ret; > +} > + > +static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) > +{ > + struct sun4i_spdif_dev *host =3D snd_soc_dai_get_drvdata(dai); > + > + snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL); > + return 0; > +} > + > +static const struct snd_soc_dai_ops sun4i_spdif_dai_ops =3D { > + .startup =3D sun4i_spdif_startup, > + .trigger =3D sun4i_spdif_trigger, > + .hw_params =3D sun4i_spdif_hw_params, > +}; > + > +static const struct regmap_config sun4i_spdif_regmap_config =3D { > + .reg_bits =3D 32, > + .reg_stride =3D 4, > + .val_bits =3D 32, > + .max_register =3D SUN4I_SPDIF_RXCHSTA1, > +}; > + > +#define SUN4I_RATES SNDRV_PCM_RATE_8000_192000 > + > +#define SUN4I_FORMATS (SNDRV_PCM_FORMAT_S16_LE | \ > + SNDRV_PCM_FORMAT_S20_3LE | \ > + SNDRV_PCM_FORMAT_S24_LE) > + > +static struct snd_soc_dai_driver sun4i_spdif_dai =3D { > + .playback =3D { > + .channels_min =3D 1, > + .channels_max =3D 2, > + .rates =3D SUN4I_RATES, > + .formats =3D SUN4I_FORMATS, > + }, > + .probe =3D sun4i_spdif_soc_dai_probe, > + .ops =3D &sun4i_spdif_dai_ops, > + .name =3D "spdif", > +}; > + > +static const struct snd_soc_dapm_widget dit_widgets[] =3D { > + SND_SOC_DAPM_OUTPUT("spdif-out"), > +}; > + > +static const struct snd_soc_dapm_route dit_routes[] =3D { > + { "spdif-out", NULL, "Playback" }, > +}; > + > +static const struct of_device_id sun4i_spdif_of_match[] =3D { > + { .compatible =3D "allwinner,sun4i-a10-spdif", }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match); > + > +static const struct snd_soc_component_driver sun4i_spdif_component =3D { > + .name =3D "sun4i-spdif", > +}; > + > +static int sun4i_spdif_runtime_suspend(struct device *dev) > +{ > + struct sun4i_spdif_dev *host =3D dev_get_drvdata(dev); > + > + clk_disable_unprepare(host->spdif_clk); > + clk_disable_unprepare(host->apb_clk); > + > + return 0; > +} > + > +static int sun4i_spdif_runtime_resume(struct device *dev) > +{ > + struct sun4i_spdif_dev *host =3D dev_get_drvdata(dev); > + > + clk_prepare_enable(host->spdif_clk); > + clk_prepare_enable(host->apb_clk); > + > + return 0; > +} > + > +static int sun4i_spdif_probe(struct platform_device *pdev) > +{ > + struct sun4i_spdif_dev *host; > + struct resource *res; > + int ret; > + void __iomem *base; > + > + dev_dbg(&pdev->dev, "Entered %s\n", __func__); > + > + host =3D devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); > + if (!host) > + return -ENOMEM; > + > + host->pdev =3D pdev; > + > + /* Initialize this copy of the CPU DAI driver structure */ > + memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai)); > + host->cpu_dai_drv.name =3D dev_name(&pdev->dev); > + > + /* Get the addresses */ > + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); > + base =3D devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(base)) > + return PTR_ERR(base); > + > + host->regmap =3D devm_regmap_init_mmio(&pdev->dev, base, > + &sun4i_spdif_regmap_config); > + > + /* Clocks */ > + host->apb_clk =3D devm_clk_get(&pdev->dev, "apb"); > + if (IS_ERR(host->apb_clk)) { > + dev_err(&pdev->dev, "failed to get a apb clock.\n"); > + return PTR_ERR(host->apb_clk); > + } > + > + ret =3D clk_prepare_enable(host->apb_clk); > + if (ret) { > + dev_err(&pdev->dev, "try to enable apb_spdif_clk failed\n"); > + return ret; > + } You don't need to enable it anymore. > + > + host->spdif_clk =3D devm_clk_get(&pdev->dev, "spdif"); > + if (IS_ERR(host->spdif_clk)) { > + dev_err(&pdev->dev, "failed to get a spdif clock.\n"); > + goto err_disable_apb_clk; > + } > + > + host->dma_params_tx.addr =3D res->start + SUN4I_SPDIF_TXFIFO; > + host->dma_params_tx.maxburst =3D 4; > + host->dma_params_tx.addr_width =3D DMA_SLAVE_BUSWIDTH_2_BYTES; > + > + platform_set_drvdata(pdev, host); > + > + ret =3D devm_snd_soc_register_component(&pdev->dev, > + &sun4i_spdif_component, &sun4i_spdif_dai, 1); > + if (ret) > + goto err_disable_apb_clk; > + > + pm_runtime_enable(&pdev->dev); > + if (!pm_runtime_enabled(&pdev->dev)) { > + ret =3D sun4i_spdif_runtime_resume(&pdev->dev); > + if (ret) > + goto err_unregister; > + } > + > + ret =3D devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); > + if (ret) > + goto err_suspend; > + return 0; > +err_suspend: > + if (!pm_runtime_status_suspended(&pdev->dev)) > + sun4i_spdif_runtime_suspend(&pdev->dev); > +err_unregister: > + pm_runtime_disable(&pdev->dev); > + snd_soc_unregister_component(&pdev->dev); > +err_disable_apb_clk: > + clk_disable_unprepare(host->apb_clk); > + return ret; > +} > + > +static int sun4i_spdif_remove(struct platform_device *pdev) > +{ > + pm_runtime_disable(&pdev->dev); > + if (!pm_runtime_status_suspended(&pdev->dev)) > + sun4i_spdif_runtime_suspend(&pdev->dev); > + > + snd_soc_unregister_platform(&pdev->dev); > + snd_soc_unregister_component(&pdev->dev); > + > + return 0; > +} > + > +static const struct dev_pm_ops sun4i_spdif_pm =3D { > + SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend, > + sun4i_spdif_runtime_resume, NULL) > +}; > + > +static struct platform_driver sun4i_spdif_driver =3D { > + .driver =3D { > + .name =3D "sun4i-spdif", > + .of_match_table =3D of_match_ptr(sun4i_spdif_of_match), > + .pm =3D &sun4i_spdif_pm, > + }, > + .probe =3D sun4i_spdif_probe, > + .remove =3D sun4i_spdif_remove, > +}; > + > +module_platform_driver(sun4i_spdif_driver); > + > +MODULE_AUTHOR("Marcus Cooper "); > +MODULE_AUTHOR("Andrea Venturi "); > +MODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:sun4i-spdif"); > --=20 > 2.7.0 >=20 Thanks! Maxime --=20 Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com --gDGSpKKIBgtShtf+ Content-Type: application/pgp-signature; name="signature.asc" Content-Description: Digital signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAEBAgAGBQJWtGsLAAoJEBx+YmzsjxAg/vUP/0QZs8cnKVvxxlBnVrfjZfJk DGgz2b3VmCe6g/ObxcWJSbdwkrBQ1i3ZzgOaCPKJ9C2iRpVStmAaVRUyrxR4TMbY tOVrukZzKzwZW5qkxpvdCWnSXaY2aZXNM4nXCYjnMTCvwR9ONBLjzZKUG2hV7Y0w h3AjPQqkgLqN2T87PTXmWqrnz64IrFRKZsCVilRsRAzq12ZWg6LNq+Hkv0ztP2c6 45bGqC/VwtBn2DXsRiw+2MYdz4oq/fccD3KBru0E0nbxI/lcsa6z/UKy9zhC1eDS e4MIddF1A2asZIb9slx4GDXWba1xRSWk/kTIfUljb1hH8Z1iEaTl18feVfkpNZBI WqRRHjTKIL7dcYoU4y4E1DttEh1eFxzwLgZjyf5lOeZsCXgSnOepgogxmwX0mIWv iddK4t7m7/vW0uGYvr/oA7aAWTSSzRERYCqnotc9l79KWCsEmyQ/TUja/eiGu6B+ 7V4yQ7GuC+0xsf2u189Lkc1haZS9WQAfEavzl+9hB15o5owdhgog0RdYMtKeleFt 1iyd9oNcP0JPdZQirLje3xUWO7JNuL2b8BZFtqzLDQ+U+JWUkA0N09ryVMYxa2jO v6Z2TWsNSSOW5plT7v5pf/yvoe6ZME2I0P4wy1NoZIfKDygeeS28SALtTF/nuB6J MCRoiRO9vn+0ZhtjSKv3 =wybO -----END PGP SIGNATURE----- --gDGSpKKIBgtShtf+--