hello, i've tested the experimental github branch for I2S DAI mainline linux driver on: https://github.com/mripard/linux/tree/sunxi/wip/a20-i2s
my HW setup actually is: - an Olimex A20-SOM with EValuation Board: https://www.olimex.com/Products/SOM/A20/A20-SOM-EVB/ - the Audio DAC is a PCM5102x based low cost device: http://www.aliexpress.com/item/PCM5102-DAC-Decoder-I2S-Player-Assembled-Board-32Bit-384K-Beyond-ES9023-PCM1794/32579339671.html let me state in advance i've no economic interest at all in these specific vendors or products! :-) BTW the PCM510x DAC is a pretty simple device with no cfg at all on the software side (there's no I2C or SPI bus [*]) and it does need only three wires on the I2S side (BITCLK, LRCLK and DATAIN) as the local system clock is "generated" internally with some kind of PLL. so, as technically speaking, the "simple sound card" need two entries for the two "legs" (DAI+CODEC) of an audio connection, i had to put in place a pretty simple "stub" driver of the PCM codec (patch 0001); this PCM510x codec driver needs to be enabled in kernel config for module creation. (also the I2S sunxi DAI has to be enabled there) then in patch 0004 there are the DTS entries for the A20-OLIMEX-SOM EVB entries enabling the I2S DAI based audio card (the patched DTS enables also the analog audio codec here) at this point, as per my tests,we could alreay have a bitstream when doing a playback with aplay but with wrong clocks (slower) on BITCLK and LRCLK pins. AFAICS this is happening for two reasons: - the 4 clock (PLL2x1 .. PLL2x8) entries in DTSI about i2s0 on the clk compatible = "allwinner,sun4i-a10-mod1-clk" are reversed , look at others clk entries for spdif/ac97 to see they do not match (patch 0002) - the mod1_clk clock composite driver was not able to set parent rate for a missing flag (patch 0003), thanx to mripard for the hint on IRC. after these four patches applied, the playback is working "flawlessly" (a part of some "scratching frames" in the playback start.. some kind of audio muting would be useful at start). a (not so subtle) issue come to mind if both analog audio codec and I2S DAI try to playback two streams with incompatible sample rate (44100 and 48000 for example), i suppose the latest starting would reprogram the PLL2 in a way not compatible with the first one. some kind of locking preventing this issue would be needed in the long run.. i post this mail for further consideration and tests from the linux-sunxi community; as i've been told by Maxime Ripard, we are still far from having a mainlining of this driver because it would need a proper submission to relevant linux group of interests and maintainers (Alsa and ARM i suppose), and that's something i'm actually not really ready to properly tackle! :-) me, i'm actually considering to test and implement also the "I2S slave mode" where clocks come from "codec" (with a different HW codec, of course and report if something useful comes out..) hope this is useful for someone, anyway. that's all, bye Andrea [*] technically speaking there's a "jumper" on the DAC board (selecting from "I2S" formatted to LEFT justified I2S data) that is "statically" forced to one level (actually i tested I2S only) and i suppose it could be configured with an additional GPIO line between SOC and DAC board as selectable on the fly at startup or maybe runtime too, but i don't see it a compelling or missing feature at all. who carez! :-) -- You received this message because you are subscribed to the Google Groups "linux-sunxi" group. To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
>From e2f3144ba703104601150345d4d6d70f57d56add Mon Sep 17 00:00:00 2001 From: Andrea Venturi <ennesimamail...@gmail.com> Date: Tue, 12 Jan 2016 14:10:22 +0100 Subject: [PATCH 4/4] add entries for enabling both I2S DAI link toward PCM510x 'dumb' codec and analog one --- arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts b/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts index b7fe102..bba3560 100644 --- a/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts +++ b/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts @@ -71,6 +71,26 @@ default-state = "on"; }; }; + + pcm5102: pcm510x { + compatible = "ti,pcm510x"; + pcm510x,format = "i2s"; + #sound-dai-cells = <0>; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + + simple-audio-card,cpu { + sound-dai = <&dai0>; + }; + + simple-audio-card,codec { + sound-dai = <&pcm5102>; + }; + }; + }; &ahci { @@ -196,3 +216,15 @@ usb2_vbus-supply = <®_usb2_vbus>; status = "okay"; }; + + +&dai0 { + pinctrl-names = "default"; + pinctrl-0 = <&dai0_pins>; + status = "okay"; +}; + +&codec { + status = "okay"; +}; + -- 2.1.4
>From 7ab9cf20dbba3aa76e379f796374d2454a0ad171 Mon Sep 17 00:00:00 2001 From: Andrea Venturi <ennesimamail...@gmail.com> Date: Tue, 12 Jan 2016 14:09:26 +0100 Subject: [PATCH 3/4] add CLK_SET_RATE_PARENT for modify rate on clk upstream --- drivers/clk/sunxi/clk-a10-mod1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c index 7a71be6..4e06132 100644 --- a/drivers/clk/sunxi/clk-a10-mod1.c +++ b/drivers/clk/sunxi/clk-a10-mod1.c @@ -62,7 +62,7 @@ static void __init sun4i_mod1_clk_setup(struct device_node *node) clk = clk_register_composite(NULL, clk_name, parents, i, &mux->hw, &clk_mux_ops, NULL, NULL, - &gate->hw, &clk_gate_ops, 0); + &gate->hw, &clk_gate_ops, CLK_SET_RATE_PARENT); if (IS_ERR(clk)) goto err_free_gate; -- 2.1.4
>From e30cca603788fcdde6ec5b606838d220971c1505 Mon Sep 17 00:00:00 2001 From: Andrea Venturi <ennesimamail...@gmail.com> Date: Tue, 12 Jan 2016 14:08:44 +0100 Subject: [PATCH 2/4] patch dtsi for PLL clk list inverted on i2s0 --- arch/arm/boot/dts/sun7i-a20.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 9c83716..d5b48a0 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -479,10 +479,10 @@ #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod1-clk"; reg = <0x01c200b8 0x4>; - clocks = <&pll2 SUN4I_A10_PLL2_1X>, - <&pll2 SUN4I_A10_PLL2_2X>, + clocks = <&pll2 SUN4I_A10_PLL2_8X>, <&pll2 SUN4I_A10_PLL2_4X>, - <&pll2 SUN4I_A10_PLL2_8X>; + <&pll2 SUN4I_A10_PLL2_2X>, + <&pll2 SUN4I_A10_PLL2_1X>; clock-output-names = "iis0"; }; -- 2.1.4
>From 3305919b9d5abafbaee0543fdf7bafa5aec9b363 Mon Sep 17 00:00:00 2001 From: Andrea Venturi <ennesimamail...@gmail.com> Date: Tue, 12 Jan 2016 13:59:54 +0100 Subject: [PATCH 1/4] add a simple codec for PCM510x codec without cfg --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/pcm510x.c | 177 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 sound/soc/codecs/pcm510x.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 6ba94a7..9c6d613 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -83,6 +83,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 + select SND_SOC_PCM510X select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT286 if I2C @@ -497,6 +498,9 @@ config SND_SOC_PCM1792A config SND_SOC_PCM3008 tristate +config SND_SOC_PCM510x + tristate "Texas Instruments PCM510x CODEC" + config SND_SOC_PCM512x tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077..5bf3c84 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -76,6 +76,7 @@ snd-soc-hdmi-codec-objs := hdmi.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o +snd-soc-pcm510x-objs := pcm510x.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o @@ -268,6 +269,7 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o +obj-$(CONFIG_SND_SOC_PCM510x) += snd-soc-pcm510x.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o diff --git a/sound/soc/codecs/pcm510x.c b/sound/soc/codecs/pcm510x.c new file mode 100644 index 0000000..02121d7 --- /dev/null +++ b/sound/soc/codecs/pcm510x.c @@ -0,0 +1,177 @@ +/* + * PCM510x ASoC codec driver + * + * Andrea Venturi <be17...@iperbole.bo.it> + * + * Borrowed from PCM510X, Copyright (c) StreamUnlimited GmbH 2013 + * Marek Belisko <marek.beli...@streamunlimited.com> + * + * 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 <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/regmap.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define PCM510X_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define PCM510X_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +struct pcm510x_platform_data { + int format; +}; +#define PCM510x_FORMAT_I2S 0 +#define PCM510x_FORMAT_LJS 1 + +struct pcm510x_private { + unsigned int format; + /* Current rate for deemphasis control */ + unsigned int rate; +}; + +static int pcm510x_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct pcm510x_private *priv = snd_soc_codec_get_drvdata(codec); + + /* The PCM510X can only be slave to all clocks */ + if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "Invalid clocking mode\n"); + return -EINVAL; + } + + priv->format = format; + + return 0; +} + +static int pcm510x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm510x_private *priv = snd_soc_codec_get_drvdata(codec); + + priv->rate = params_rate(params); + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops pcm510x_dai_ops = { + .set_fmt = pcm510x_set_dai_fmt, + .hw_params = pcm510x_hw_params, +}; + +static const struct snd_soc_dapm_widget pcm510x_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("VOUT1"), +SND_SOC_DAPM_OUTPUT("VOUT2"), +}; + +static const struct snd_soc_dapm_route pcm510x_dapm_routes[] = { + { "VOUT1", NULL, "Playback" }, + { "VOUT2", NULL, "Playback" }, +}; + +static const struct snd_kcontrol_new pcm510x_controls[] = { +}; + +static struct snd_soc_dai_driver pcm510x_dai = { + .name = "pcm510x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = PCM510X_PCM_RATES, + .formats = PCM510X_PCM_FORMATS, + }, + .ops = &pcm510x_dai_ops, +}; + +#ifdef CONFIG_OF +static const struct of_device_id pcm510x_dt_ids[] = { + { .compatible = "ti,pcm510x", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm510x_dt_ids); +#endif + +static const struct regmap_config pcm510x_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0, +}; + +static struct snd_soc_codec_driver soc_codec_dev_pcm510x = { + .controls = pcm510x_controls, + .num_controls = ARRAY_SIZE(pcm510x_controls), + .dapm_widgets = pcm510x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm510x_dapm_widgets), + .dapm_routes = pcm510x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm510x_dapm_routes), +}; + +static int pcm510x_probe(struct platform_device *pdev) +{ + //int ret; + //struct pcm510x_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct pcm510x_private *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm510x, &pcm510x_dai, 1); +} + +static int pcm510x_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver pcm510x_driver = { + .driver = { + .name = "pcm510x", + .of_match_table = of_match_ptr(pcm510x_dt_ids), + }, + .probe = pcm510x_probe, + .remove = pcm510x_remove, +}; + +module_platform_driver(pcm510x_driver); + +MODULE_DESCRIPTION("Texas Instruments PCM510X ALSA SoC Codec Driver"); +MODULE_AUTHOR("Andrea Venturi <be17...@iperbole.bo.it>"); +MODULE_LICENSE("GPL"); -- 2.1.4