User avatar
audio-badger
Posts: 15
Joined: Wed Jun 15, 2016 9:44 am

Re: I2S Success (at last) !

Mon Jun 20, 2016 5:01 pm

OK, after much fiddling and lots of googling, I have I2S input (kind of) working on RPI3 in Jessie 4.4.11-v7. So I can reassure others that this does work. If anyone is interested, I can post the steps.

I have changed the PI to be master of both BCLK and LRCLK using:

Code: Select all

.daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS,
This causes both BCLK and LRCLK to be driven :)

However, something very odd is happening. BCLK is exactly 100x LRCLOCK. Last time I checked, I2S uses 32b per channel, giving a 64b frame size.

Any clues from you PI/I2S graduates out there?

Clearly the I2S peripheral has been configured to put too many clocks.. I can see in the BCM2835 datasheet there is a register "ODE_A Register" with field "FLEN" which sets the frame length. This is clearly set wrong.

What's the best way to access and tweak this register? Ideally in the driver so it's permanent..

User avatar
audio-badger
Posts: 15
Joined: Wed Jun 15, 2016 9:44 am

Re: I2S Success (at last) !

Wed Jun 22, 2016 10:00 am

Didn't get any pointers so had to get deep and dirty in the code... :geek:

Anyhow, turns out that simple-card.c doesn't set the frame:bclk ratio at all, leaving a rather odd default of 100 for 32b audio and 50 for 16b audio. So I recompiled simple-card.c with the following line at the end of asoc_simple_card_dai_init():

Code: Select all

   ret = snd_soc_dai_set_bclk_ratio(cpu, 64);
then rmmod and insmod'd it back in and hey presto - 64b I2S frame! All working beautifully now. :D

Since then I have found this https://github.com/raspberrypi/linux/is ... t-55153828 which explains why the odd number of 50 was chosen.

Anyways, that default broke things for me but a the above mod fixed it.

Hope this is useful for others.

vjvarada
Posts: 14
Joined: Wed Mar 16, 2016 7:12 pm

Re: I2S Success (at last) !

Sun Jun 26, 2016 2:40 pm

audio-badger wrote:Didn't get any pointers so had to get deep and dirty in the code... :geek:

Anyhow, turns out that simple-card.c doesn't set the frame:bclk ratio at all, leaving a rather odd default of 100 for 32b audio and 50 for 16b audio. So I recompiled simple-card.c with the following line at the end of asoc_simple_card_dai_init():

Code: Select all

   ret = snd_soc_dai_set_bclk_ratio(cpu, 64);
then rmmod and insmod'd it back in and hey presto - 64b I2S frame! All working beautifully now. :D

Since then I have found this https://github.com/raspberrypi/linux/is ... t-55153828 which explains why the odd number of 50 was chosen.

Anyways, that default broke things for me but a the above mod fixed it.

Hope this is useful for others.

Congratulations on your success!
Could you go into a little detail of recompiling simple-card.c, and where the output needs to be placed? thanks in advance :)
Ive got a knowels i2s mic recording voices like Darth Vader's, and i think changing BCLK will fix the issue.

User avatar
audio-badger
Posts: 15
Joined: Wed Jun 15, 2016 9:44 am

Re: I2S Success (at last) !

Tue Jun 28, 2016 5:17 am

If you have sound but it's gone all dalek then it will definitely be something basic about I2S signals. Have you counted the BCLKs per LRCLK frame?
Could you go into a little detail of recompiling simple-card.c, and where the output needs to be placed? thanks in advance :)
Sure! Firstly, presumably you changed the my_loader.c stuff so that the RPI is I2S master? I expect your mics are slave? It's the CBS (codec BCLK slave) and CFS (coded FRAME CLK slave) bits that are key.

Code: Select all

   .daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS,
I had to recompile simple-card.c to make the BCLK ratio mod. There may be other ways, but it worked for me. Do it exactly the same way as for my_loader..

Something like this:

Copy simple-card.c source file to a working directory (must be different from my_loader)

To make the 64b correction, at the end of function ``asoc_simple_card_dai_init`` around line number 213, add the following source lines::

Code: Select all

   ret = snd_soc_dai_set_bclk_ratio(cpu, 64);
   pr_alert("BCLK ratio set to 64!\n");
Use this makefile to build the module.
i.e. copy the contents into ``Makefile``. Note that the last line must be indented using a tab character rather than using spaces to avoid a build error. Note that I found the one liner Makefile shown in various threads didn't work, but this did..

Code: Select all

   obj-m := simple_card.o
   KDIR := /lib/modules/$(shell uname -r)/build
   PWD := $(shell pwd)
   default:
          $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
And finally build the module, insert it into the kernel, and prepare for the next step::

Code: Select all

   make
   sudo insmod simple_card.ko
After this, my sound card appeared using arecord -l and all was well with the world! *

*After many hours of face-palming and swearing..Oh the hours you can spend fiddling with RPIs..

Code: Select all

   **** List of CAPTURE Hardware Devices ****
   card 1: sndrpisimplecar [snd_rpi_simple_card], device 0: simple-card_codec_link snd-soc-dummy-dai-0 []
     Subdevices: 1/1
     Subdevice #0: subdevice #0
I developed this as part of an appnote connecting a smart microphone using an array of PDM mics (with beamforming, noise suppression, AGC, AEC etc.) to RPI3 for Voice Digital Assistant (like Amazon Echo etc.). I will link in when done...

vjvarada
Posts: 14
Joined: Wed Mar 16, 2016 7:12 pm

Re: I2S Success (at last) !

Sun Jul 17, 2016 6:36 pm

Thanks @audio-badger it worked!!

Some observations:

I'm using a Knowles i2S Microphone SPH0645LM4H-BRevB

After the change to the simple-soundcard, i was still not ablet o get output, but my logic analyser at least showed be a good bclk ratio of about ~62.5

I wasn't able to record anything while the logic analyser was connected for some reason ( caused a couple of hours of frustration)

I wasn't able to record in S24_BE, as the datasheet suggests, after almost loosing all hope, I tried S16_LE and works perfectly!

arecord -D plughw:1 -c1 -r 48000 -f S16_LE -t wav -v file.wav

desant
Posts: 2
Joined: Thu Jul 28, 2016 2:22 pm

Re: I2S Success (at last) !

Fri Jul 29, 2016 1:28 pm

Hello everybody,

I have to make a project for highschool to create an 'audio interface' for the rpi.

I capture the sound with a dynamic microphone, then I have two stages of amplification and then entering the ADC - PCM1804 from TI.

I want to communicate with the rpi via I2s interface. I have provided a 12.228MHz oscillator to the ADC on the system clock, in order to make it the master.

I have a fresh install of RASPBIAN JESSIE with Kernel version 4.4 on my Raspberry Pi.

Can you please guide me on how to pair the ADC board with the rpi? I'm not very familiar with all the software stuff that it's needed.

Thank you very much!

Cowjan
Posts: 2
Joined: Mon Aug 01, 2016 2:52 pm

Re: I2S Success (at last) !

Mon Aug 01, 2016 3:56 pm

Hey audio-badger, could you please post your copy of simple-card.c for reference? Thanks!

User avatar
audio-badger
Posts: 15
Joined: Wed Jun 15, 2016 9:44 am

Re: I2S Success (at last) !

Tue Aug 02, 2016 8:08 am

Yep - sure here it is. If you do a diff against the umodified kernel source you can see only two lines added, one of which is a print..

Code: Select all

/*
 * ASoC simple sound card support
 *
 * Copyright (C) 2012 Renesas Solutions Corp.
 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <sound/jack.h>
#include <sound/simple_card.h>
#include <sound/soc-dai.h>
#include <sound/soc.h>

struct simple_card_data {
	struct snd_soc_card snd_card;
	struct simple_dai_props {
		struct asoc_simple_dai cpu_dai;
		struct asoc_simple_dai codec_dai;
		unsigned int mclk_fs;
	} *dai_props;
	unsigned int mclk_fs;
	int gpio_hp_det;
	int gpio_hp_det_invert;
	int gpio_mic_det;
	int gpio_mic_det_invert;
	struct snd_soc_dai_link dai_link[];	/* dynamically allocated */
};

#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
#define simple_priv_to_props(priv, i) ((priv)->dai_props + i)

static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
	struct simple_dai_props *dai_props =
		&priv->dai_props[rtd - rtd->card->rtd];
	int ret;

	ret = clk_prepare_enable(dai_props->cpu_dai.clk);
	if (ret)
		return ret;
	
	ret = clk_prepare_enable(dai_props->codec_dai.clk);
	if (ret)
		clk_disable_unprepare(dai_props->cpu_dai.clk);

	return ret;
}

static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
	struct simple_dai_props *dai_props =
		&priv->dai_props[rtd - rtd->card->rtd];

	clk_disable_unprepare(dai_props->cpu_dai.clk);

	clk_disable_unprepare(dai_props->codec_dai.clk);
}

static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
				      struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
	struct simple_dai_props *dai_props =
		&priv->dai_props[rtd - rtd->card->rtd];
	unsigned int mclk, mclk_fs = 0;
	int ret = 0;

	if (priv->mclk_fs)
		mclk_fs = priv->mclk_fs;
	else if (dai_props->mclk_fs)
		mclk_fs = dai_props->mclk_fs;

	if (mclk_fs) {
		mclk = params_rate(params) * mclk_fs;
		ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
					     SND_SOC_CLOCK_IN);
		if (ret && ret != -ENOTSUPP)
			goto err;

		ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
					     SND_SOC_CLOCK_OUT);
		if (ret && ret != -ENOTSUPP)
			goto err;
	}

err:
	return ret;
}

static struct snd_soc_ops asoc_simple_card_ops = {
	.startup = asoc_simple_card_startup,
	.shutdown = asoc_simple_card_shutdown,
	.hw_params = asoc_simple_card_hw_params,
};

static struct snd_soc_jack simple_card_hp_jack;
static struct snd_soc_jack_pin simple_card_hp_jack_pins[] = {
	{
		.pin = "Headphones",
		.mask = SND_JACK_HEADPHONE,
	},
};
static struct snd_soc_jack_gpio simple_card_hp_jack_gpio = {
	.name = "Headphone detection",
	.report = SND_JACK_HEADPHONE,
	.debounce_time = 150,
};

static struct snd_soc_jack simple_card_mic_jack;
static struct snd_soc_jack_pin simple_card_mic_jack_pins[] = {
	{
		.pin = "Mic Jack",
		.mask = SND_JACK_MICROPHONE,
	},
};
static struct snd_soc_jack_gpio simple_card_mic_jack_gpio = {
	.name = "Mic detection",
	.report = SND_JACK_MICROPHONE,
	.debounce_time = 150,
};

static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
				       struct asoc_simple_dai *set)
{
	int ret;

	if (set->sysclk) {
		ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
		if (ret && ret != -ENOTSUPP) {
			dev_err(dai->dev, "simple-card: set_sysclk error\n");
			goto err;
		}
	}

	if (set->slots) {
		ret = snd_soc_dai_set_tdm_slot(dai,
					       set->tx_slot_mask,
					       set->rx_slot_mask,
						set->slots,
						set->slot_width);
		if (ret && ret != -ENOTSUPP) {
			dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
			goto err;
		}
	}

	ret = 0;

err:
	return ret;
}

static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
{
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
	struct snd_soc_dai *codec = rtd->codec_dai;
	struct snd_soc_dai *cpu = rtd->cpu_dai;
	struct simple_dai_props *dai_props;
	int num, ret;

	num = rtd - rtd->card->rtd;
	dai_props = &priv->dai_props[num];
	ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai);
	if (ret < 0)
		return ret;

	ret = __asoc_simple_card_dai_init(cpu, &dai_props->cpu_dai);
	if (ret < 0)
		return ret;

	if (gpio_is_valid(priv->gpio_hp_det)) {
		snd_soc_card_jack_new(rtd->card, "Headphones",
				      SND_JACK_HEADPHONE,
				      &simple_card_hp_jack,
				      simple_card_hp_jack_pins,
				      ARRAY_SIZE(simple_card_hp_jack_pins));

		simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
		simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert;
		snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
				       &simple_card_hp_jack_gpio);
	}

	if (gpio_is_valid(priv->gpio_mic_det)) {
		snd_soc_card_jack_new(rtd->card, "Mic Jack",
				      SND_JACK_MICROPHONE,
				      &simple_card_mic_jack,
				      simple_card_mic_jack_pins,
				      ARRAY_SIZE(simple_card_mic_jack_pins));
		simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
		simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert;
		snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
				       &simple_card_mic_jack_gpio);
	}
   	
	ret = snd_soc_dai_set_bclk_ratio(cpu, 64);
   	pr_alert("BCLK ratio set to 64!\n");

	return 0;
}

static int
asoc_simple_card_sub_parse_of(struct device_node *np,
			      struct asoc_simple_dai *dai,
			      struct device_node **p_node,
			      const char **name,
			      int *args_count)
{
	struct of_phandle_args args;
	struct clk *clk;
	u32 val;
	int ret;

	/*
	 * Get node via "sound-dai = <&phandle port>"
	 * it will be used as xxx_of_node on soc_bind_dai_link()
	 */
	ret = of_parse_phandle_with_args(np, "sound-dai",
					 "#sound-dai-cells", 0, &args);
	if (ret)
		return ret;

	*p_node = args.np;

	if (args_count)
		*args_count = args.args_count;

	/* Get dai->name */
	ret = snd_soc_of_get_dai_name(np, name);
	if (ret < 0)
		return ret;

	/* Parse TDM slot */
	ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
					&dai->rx_slot_mask,
					&dai->slots, &dai->slot_width);
	if (ret)
		return ret;

	/*
	 * Parse dai->sysclk come from "clocks = <&xxx>"
	 * (if system has common clock)
	 *  or "system-clock-frequency = <xxx>"
	 *  or device's module clock.
	 */
	if (of_property_read_bool(np, "clocks")) {
		clk = of_clk_get(np, 0);
		if (IS_ERR(clk)) {
			ret = PTR_ERR(clk);
			return ret;
		}

		dai->sysclk = clk_get_rate(clk);
		dai->clk = clk;
	} else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
		dai->sysclk = val;
	} else {
		clk = of_clk_get(args.np, 0);
		if (!IS_ERR(clk))
			dai->sysclk = clk_get_rate(clk);
	}

	return 0;
}

static int asoc_simple_card_parse_daifmt(struct device_node *node,
					 struct simple_card_data *priv,
					 struct device_node *codec,
					 char *prefix, int idx)
{
	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
	struct device *dev = simple_priv_to_dev(priv);
	struct device_node *bitclkmaster = NULL;
	struct device_node *framemaster = NULL;
	unsigned int daifmt;

	daifmt = snd_soc_of_parse_daifmt(node, prefix,
					 &bitclkmaster, &framemaster);
	daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;

	if (strlen(prefix) && !bitclkmaster && !framemaster) {
		/*
		 * No dai-link level and master setting was not found from
		 * sound node level, revert back to legacy DT parsing and
		 * take the settings from codec node.
		 */
		dev_dbg(dev, "Revert to legacy daifmt parsing\n");

		daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
			(daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
	} else {
		if (codec == bitclkmaster)
			daifmt |= (codec == framemaster) ?
				SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
		else
			daifmt |= (codec == framemaster) ?
				SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
	}

	daifmt |= SND_SOC_DAIFMT_CONT;	//Do not disable clocks	

	dai_link->dai_fmt = daifmt;

	of_node_put(bitclkmaster);
	of_node_put(framemaster);

	return 0;
}

static int asoc_simple_card_dai_link_of(struct device_node *node,
					struct simple_card_data *priv,
					int idx,
					bool is_top_level_node)
{
	struct device *dev = simple_priv_to_dev(priv);
	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
	struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
	struct device_node *cpu = NULL;
	struct device_node *plat = NULL;
	struct device_node *codec = NULL;
	char *name;
	char prop[128];
	char *prefix = "";
	int ret, cpu_args;
	u32 val;

	/* For single DAI link & old style of DT node */
	if (is_top_level_node)
		prefix = "simple-audio-card,";

	snprintf(prop, sizeof(prop), "%scpu", prefix);
	cpu = of_get_child_by_name(node, prop);

	snprintf(prop, sizeof(prop), "%splat", prefix);
	plat = of_get_child_by_name(node, prop);

	snprintf(prop, sizeof(prop), "%scodec", prefix);
	codec = of_get_child_by_name(node, prop);

	if (!cpu || !codec) {
		ret = -EINVAL;
		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
		goto dai_link_of_err;
	}

	ret = asoc_simple_card_parse_daifmt(node, priv,
					    codec, prefix, idx);
	if (ret < 0)
		goto dai_link_of_err;

	if (!of_property_read_u32(node, "mclk-fs", &val))
		dai_props->mclk_fs = val;

	ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
					    &dai_link->cpu_of_node,
					    &dai_link->cpu_dai_name,
					    &cpu_args);
	if (ret < 0)
		goto dai_link_of_err;

	ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai,
					    &dai_link->codec_of_node,
					    &dai_link->codec_dai_name, NULL);
	if (ret < 0)
		goto dai_link_of_err;

	if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
		ret = -EINVAL;
		goto dai_link_of_err;
	}

	if (plat) {
		struct of_phandle_args args;

		ret = of_parse_phandle_with_args(plat, "sound-dai",
						 "#sound-dai-cells", 0, &args);
		dai_link->platform_of_node = args.np;
	} else {
		/* Assumes platform == cpu */
		dai_link->platform_of_node = dai_link->cpu_of_node;
	}

	/* DAI link name is created from CPU/CODEC dai name */
	name = devm_kzalloc(dev,
			    strlen(dai_link->cpu_dai_name)   +
			    strlen(dai_link->codec_dai_name) + 2,
			    GFP_KERNEL);
	if (!name) {
		ret = -ENOMEM;
		goto dai_link_of_err;
	}

	sprintf(name, "%s-%s", dai_link->cpu_dai_name,
				dai_link->codec_dai_name);
	dai_link->name = dai_link->stream_name = name;
	dai_link->ops = &asoc_simple_card_ops;
	dai_link->init = asoc_simple_card_dai_init;

	dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
	dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
	dev_dbg(dev, "\tcpu : %s / %d\n",
		dai_link->cpu_dai_name,
		dai_props->cpu_dai.sysclk);
	dev_dbg(dev, "\tcodec : %s / %d\n",
		dai_link->codec_dai_name,
		dai_props->codec_dai.sysclk);

	/*
	 * In soc_bind_dai_link() will check cpu name after
	 * of_node matching if dai_link has cpu_dai_name.
	 * but, it will never match if name was created by
	 * fmt_single_name() remove cpu_dai_name if cpu_args
	 * was 0. See:
	 *	fmt_single_name()
	 *	fmt_multiple_name()
	 */
	if (!cpu_args)
		dai_link->cpu_dai_name = NULL;

dai_link_of_err:
	of_node_put(cpu);
	of_node_put(codec);

	return ret;
}

static int asoc_simple_card_parse_of(struct device_node *node,
				     struct simple_card_data *priv)
{
	struct device *dev = simple_priv_to_dev(priv);
	enum of_gpio_flags flags;
	u32 val;
	int ret;

	if (!node)
		return -EINVAL;

	/* Parse the card name from DT */
	snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");

	/* The off-codec widgets */
	if (of_property_read_bool(node, "simple-audio-card,widgets")) {
		ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
					"simple-audio-card,widgets");
		if (ret)
			return ret;
	}

	/* DAPM routes */
	if (of_property_read_bool(node, "simple-audio-card,routing")) {
		ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
					"simple-audio-card,routing");
		if (ret)
			return ret;
	}

	/* Factor to mclk, used in hw_params() */
	ret = of_property_read_u32(node, "simple-audio-card,mclk-fs", &val);
	if (ret == 0)
		priv->mclk_fs = val;

	dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
		priv->snd_card.name : "");

	/* Single/Muti DAI link(s) & New style of DT node */
	if (of_get_child_by_name(node, "simple-audio-card,dai-link")) {
		struct device_node *np = NULL;
		int i = 0;

		for_each_child_of_node(node, np) {
			dev_dbg(dev, "\tlink %d:\n", i);
			ret = asoc_simple_card_dai_link_of(np, priv,
							   i, false);
			if (ret < 0) {
				of_node_put(np);
				return ret;
			}
			i++;
		}
	} else {
		/* For single DAI link & old style of DT node */
		ret = asoc_simple_card_dai_link_of(node, priv, 0, true);
		if (ret < 0)
			return ret;
	}

	priv->gpio_hp_det = of_get_named_gpio_flags(node,
				"simple-audio-card,hp-det-gpio", 0, &flags);
	priv->gpio_hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
	if (priv->gpio_hp_det == -EPROBE_DEFER)
		return -EPROBE_DEFER;

	priv->gpio_mic_det = of_get_named_gpio_flags(node,
				"simple-audio-card,mic-det-gpio", 0, &flags);
	priv->gpio_mic_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
	if (priv->gpio_mic_det == -EPROBE_DEFER)
		return -EPROBE_DEFER;

	if (!priv->snd_card.name)
		priv->snd_card.name = priv->snd_card.dai_link->name;

	return 0;
}

/* Decrease the reference count of the device nodes */
static int asoc_simple_card_unref(struct snd_soc_card *card)
{
	struct snd_soc_dai_link *dai_link;
	int num_links;

	for (num_links = 0, dai_link = card->dai_link;
	     num_links < card->num_links;
	     num_links++, dai_link++) {
		of_node_put(dai_link->cpu_of_node);
		of_node_put(dai_link->codec_of_node);
	}
	return 0;
}

static int asoc_simple_card_probe(struct platform_device *pdev)
{
	struct simple_card_data *priv;
	struct snd_soc_dai_link *dai_link;
	struct device_node *np = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
	int num_links, ret;

	/* Get the number of DAI links */
	if (np && of_get_child_by_name(np, "simple-audio-card,dai-link"))
		num_links = of_get_child_count(np);
	else
		num_links = 1;

	/* Allocate the private data and the DAI link array */
	priv = devm_kzalloc(dev,
			sizeof(*priv) + sizeof(*dai_link) * num_links,
			GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	/* Init snd_soc_card */
	priv->snd_card.owner = THIS_MODULE;
	priv->snd_card.dev = dev;
	dai_link = priv->dai_link;
	priv->snd_card.dai_link = dai_link;
	priv->snd_card.num_links = num_links;

	priv->gpio_hp_det = -ENOENT;
	priv->gpio_mic_det = -ENOENT;

	/* Get room for the other properties */
	priv->dai_props = devm_kzalloc(dev,
			sizeof(*priv->dai_props) * num_links,
			GFP_KERNEL);
	if (!priv->dai_props)
		return -ENOMEM;

	if (np && of_device_is_available(np)) {

		ret = asoc_simple_card_parse_of(np, priv);
		if (ret < 0) {
			if (ret != -EPROBE_DEFER)
				dev_err(dev, "parse error %d\n", ret);
			goto err;
		}

	} else {
		struct asoc_simple_card_info *cinfo;

		cinfo = dev->platform_data;
		if (!cinfo) {
			dev_err(dev, "no info for asoc-simple-card\n");
			return -EINVAL;
		}

		if (!cinfo->name ||
		    !cinfo->codec_dai.name ||
		    !cinfo->codec ||
		    !cinfo->platform ||
		    !cinfo->cpu_dai.name) {
			dev_err(dev, "insufficient asoc_simple_card_info settings\n");
			return -EINVAL;
		}

		priv->snd_card.name	= (cinfo->card) ? cinfo->card : cinfo->name;
		dai_link->name		= cinfo->name;
		dai_link->stream_name	= cinfo->name;
		dai_link->platform_name	= cinfo->platform;
		dai_link->codec_name	= cinfo->codec;
		dai_link->cpu_dai_name	= cinfo->cpu_dai.name;
		dai_link->codec_dai_name = cinfo->codec_dai.name;
		dai_link->dai_fmt	= cinfo->daifmt;
		dai_link->init		= asoc_simple_card_dai_init;
		memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
					sizeof(priv->dai_props->cpu_dai));
		memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
					sizeof(priv->dai_props->codec_dai));

	}

	snd_soc_card_set_drvdata(&priv->snd_card, priv);

	ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
	if (ret >= 0)
		return ret;

err:
	asoc_simple_card_unref(&priv->snd_card);
	return ret;
}

static int asoc_simple_card_remove(struct platform_device *pdev)
{
	struct snd_soc_card *card = platform_get_drvdata(pdev);
	struct simple_card_data *priv = snd_soc_card_get_drvdata(card);

	if (gpio_is_valid(priv->gpio_hp_det))
		snd_soc_jack_free_gpios(&simple_card_hp_jack, 1,
					&simple_card_hp_jack_gpio);
	if (gpio_is_valid(priv->gpio_mic_det))
		snd_soc_jack_free_gpios(&simple_card_mic_jack, 1,
					&simple_card_mic_jack_gpio);

	return asoc_simple_card_unref(card);
}

static const struct of_device_id asoc_simple_of_match[] = {
	{ .compatible = "simple-audio-card", },
	{},
};
MODULE_DEVICE_TABLE(of, asoc_simple_of_match);

static struct platform_driver asoc_simple_card = {
	.driver = {
		.name = "asoc-simple-card",
		.of_match_table = asoc_simple_of_match,
	},
	.probe = asoc_simple_card_probe,
	.remove = asoc_simple_card_remove,
};

module_platform_driver(asoc_simple_card);

MODULE_ALIAS("platform:asoc-simple-card");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ASoC Simple Sound Card");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");

Jowls
Posts: 4
Joined: Tue Aug 02, 2016 11:35 am

Re: I2S Success (at last) !

Tue Aug 02, 2016 11:39 am

Hi audio-badger, your application sounds very interesting. Are you suggesting you've got multiple channels (i.e. more than a stereo pair) working with I2S? I'm interested in measuring 4 channels simultaneously but wasn't sure if this was possible with I2S.

Thanks

Cowjan
Posts: 2
Joined: Mon Aug 01, 2016 2:52 pm

Re: I2S Success (at last) !

Tue Aug 02, 2016 1:54 pm

@audio-badger, thanks! I recompiled it successfully, insmodded that and my_loader, however when I try to record I get an error:

Code: Select all

$ arecord -D hw:1 -f S24_LE -c2 -r 48000 sample.wav
Recording WAVE 'sample.wav' : Signed 24 bit Little Endian, Rate 48000 Hz, Stereo
arecord: pcm_read:2031: read error: Input/output error
Then I notice in syslog I see this:

Code: Select all

$ tail /var/log/syslog
Aug  2 13:45:56 raspberrypi kernel: [  771.038565] bcm2835-dma 3f007000.dma: DMA transfer could not be terminated
Aug  2 13:46:13 raspberrypi kernel: [  788.107334] bcm2835-i2s 3f203000.i2s: I2S SYNC error!
I'm just wondering if you had any of these issues ;)

User avatar
audio-badger
Posts: 15
Joined: Wed Jun 15, 2016 9:44 am

Re: I2S Success (at last) !

Wed Aug 03, 2016 3:51 pm

Sorry - can't help much as mine just worked:

Code: Select all

pi@raspberrypi:~ $ arecord -D hw:2 -f S24_LE -c2 -r 48000 test.wav
Recording WAVE 'test.wav' : Signed 24 bit Little Endian, Rate 48000 Hz, Stereo
^CAborted by signal Interrupt...
That error looks like sync is missing:

Code: Select all

	/* Wait for the SYNC flag changing it's state */
	while (--timeout) {
		regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
		if ((csreg & BCM2835_I2S_SYNC) != syncval)
			break;
	}

	if (!timeout)
		dev_err(dev->dev, "I2S SYNC error!\n");

Did you setup the PI as master (codec is slave) for Frame clock and bit clock?
.daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS,

User avatar
audio-badger
Posts: 15
Joined: Wed Jun 15, 2016 9:44 am

Re: I2S Success (at last) !

Wed Aug 03, 2016 3:54 pm

Are you suggesting you've got multiple channels (i.e. more than a stereo pair) working with I2S?
Nope - I'm doing all of the smart stuff in an external audio processor and sending a mono, pre-processed stream.

I'm not sure if the BCM2835 peripheral can do TDM (which is probably what you need to get more than the 2 channels supported by I2S). The alternative is to run at double the sample rate and manually interleave samples. Will need some logic at the send end to do that though.. Or maybe a nice multichannel I2S to USB converter chip..

Jowls
Posts: 4
Joined: Tue Aug 02, 2016 11:35 am

Re: I2S Success (at last) !

Fri Aug 12, 2016 8:32 am

Thanks for the response - sorry not to reply sooner, I thought I would get an email when someone replied. Showing my forum inexperience here!!
audio-badger wrote:The alternative is to run at double the sample rate and manually interleave samples. Will need some logic at the send end to do that though.. Or maybe a nice multichannel I2S to USB converter chip..
I had assumed that is what I might need to do - thanks for confirming. Whilst I fully understand that method in principle, I currently have no idea how to go about it. Are there ICs that would interleave two I2S streams? That would be the simplest route in my mind. e.g. two stereo 48 kHz I2S streams going in, then one 96 kHz interleaved stream coming out. It would then be easy to split the streams in software once it's captured. I've tried a Google search but haven't found anything particularly obvious yet. If you could point me in the right direction that would be greatly appreciated!

Jowls
Posts: 4
Joined: Tue Aug 02, 2016 11:35 am

Re: I2S Success (at last) !

Fri Aug 12, 2016 8:33 am

Just seen the "Watch" button!

User avatar
audio-badger
Posts: 15
Joined: Wed Jun 15, 2016 9:44 am

Re: I2S Success (at last) !

Mon Aug 15, 2016 10:40 am

Starting go a bit off topic here, but in response to:
Are there ICs that would interleave two I2S streams? That would be the simplest route in my mind. e.g. two stereo 48 kHz I2S streams going in, then one 96 kHz interleaved stream coming out. It would then be easy to split the streams in software once it's captured. I've tried a Google search but haven't found anything particularly obvious yet. If you could point me in the right direction that would be greatly appreciated!
You'd probably need to do something custom with a pair of I2S interfaces instantiated and simple logic to do the intervleaving - kind of like a 2:1 I2S SERDES. A small CPLD would easily handle this or, being careful to not plug the company I work for, a small Xmos device. The issue will be startup sync though - working out which pair of channels was presented first. Not sure how to solve that without putting in logic in the SERDES to present a known data value after reset.

However, if the overall goal is to get two I2S streams into the RPI, then it may be just as easy/cost effective to use a USB to I2S bridge and then you can rely on the built-in USB audio drivers in Jessie (although personally have not tested 4ch operation - this would need checking). You can get 4ch versions from companies from C-media as well as getting an Xmos chip + free USB Audio firmware.

[Edit] - I just had 10 channel input working fine with a USB audio class 2 soundcard, so 4ch should be fine

Jowls
Posts: 4
Joined: Tue Aug 02, 2016 11:35 am

Re: I2S Success (at last) !

Mon Aug 15, 2016 1:42 pm

Thanks a lot audio-badger, really appreciate the help. Just done a bit of reading up on CPLDs and it looks a bit daunting! Will see if I can make any headway with that. The USB/I2S bridge is a good option although I was hoping to avoid USB as access to the USB ports is tricky with the way we have the Pi mounted currently. I could always desolder one of the USB ports and do an internal connection but that's quite tricky with such a big heatsink in the port casing!

Anyway, I will investigate further and distract this thread no longer. Thanks again audio-badger for your help.

User avatar
audio-badger
Posts: 15
Joined: Wed Jun 15, 2016 9:44 am

Re: I2S Success (at last) !

Fri Aug 19, 2016 10:14 am

Hi to anyone wanting to enable generic I2S input and output on the RPI! Based on lot's of people's great work in this thread and others, I've compiled a step by step guide for getting this working and added updates where necessary. The end goal for me was a mono 16KHz I2S mic input to work with the Amazon Alexa app, but it worked for other sample rates, 2 channels, and output too. This is for pure I2S - any I2C or SPI configuration you may need will have to be written separately, but this isn't too hard and there are lots of threads on how to do that.

It is all built and tested on RPI3 running a fresh noobs and jessie 4.4.13v7 .

Enjoy :)

Hopefully this will save others days of fiddling around trying to get this working! Apologies for long post in .rst format embedded within a code block - site wouldn't let me attach a pdf or even txt and if I just posted the raw text then all of the indents got stripped - not ideal for makefiles etc!! Pinout attached too.

------------------------------------------------------------------------------------------------------

Code: Select all

Enabling I2S
............

The default ALSA sound devices supported by the Raspberry Pi 3 are PWM audio output jack and HDMI output. To run this demonstration I2S input and output needs to be enabled. The goal is to use a simple I2S interface that does not depend on a particular DAC or CODEC device that requires configuration over I2C or SPI.

To avoid recompilation of the Linux kernel a loadable kernel module is used.  This enables the driver for the I2S peripheral inside the BCM2708 device to be loaded dynamically.

A full description of how to do this can be found in the link below [#]_, however a brief summary of the required steps, found to work on a clean copy of Debian Jessie shipped with ``Noobs``, is show below:

.. [#] http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=91237

First, update the Raspbian Linux distribution to the latest version (version 4.4.13v7 tested). This may take a few hours::

   sudo apt-get update
   sudo apt-get dist-upgrade

Before proceeding with the next steps, ensure you have re-booted your Raspberry Pi so that it is running the latest version of Raspbian that you have just downloaded. This can be done using the following command::

   sudo reboot

Next, we need to enable I2S in device tree. To do this, edit the file ``/boot/config.txt`` to remove the comment on line 47::

   dtparam=i2s=on

The sound card driver depends on the I2S peripheral and Direct Memory Access Controller modules which need to be added to  file. To do this, add the following lines to ``/etc/modules`` using your favorite text editor prefixed with ``sudo`` to ensure super user privileges::

   snd_soc_bcm2708
   snd_soc_bcm2708_i2s
   bcm2708_dmaengine

Next, download the kernel source (this may also take a long time to download and extract). The source download script requires the installation of two helper applications that are not installed by default. The installation of these is included below::

   sudo apt-get install bc
   sudo apt-get install libncurses5-dev
   git clone http://github.com/notro/rpi-source
   cd rpi-source
   python rpi-source
   cd ..

Now we have the kernel source downloaded, we can modify the driver source and build the modules needed to enable I2S. We require two modules --- one called ``loader`` which instantiates the sound-card module and the other is ``asoc_simple_card`` which is the sound card driver itself.

For further information on building and loading modules under Linux the link below provides a useful reference [#]_. 

.. [#] http://www.cyberciti.biz/tips/build-linux-kernel-module-against-installed-kernel-source-tree.html

Next we need to build the simple sound card driver::

   mkdir snd_driver
   cd snd_driver
   cp ../linux/sound/soc/generic/simple-card.c ./asoc_simple_card.c

Note that the directory ``linux`` will appended with a long hash. Please use this directory in the above copy command.
E.g. ``/linux-e88b69aa792dbe2b289ff528c0d07303068aa0aa/``.

The file needs a small modification to work around a bug (uninitialized BCLK ratio, causing the default of 100 bit clocks per I2S frame rather than the required 64 bit). To make this correction, at the end of function ``asoc_simple_card_dai_init`` around line number 213, add the following  source lines::

   ret = snd_soc_dai_set_bclk_ratio(cpu, 64);
   pr_alert("BCLK ratio set to 64!\n");

Use this makefile to build the module.
i.e. copy the contents into ``Makefile``. Note that the last line must be indented using a tab character rather than using spaces to avoid a build error::

   obj-m := asoc_simple_card.o
   KDIR := /lib/modules/$(shell uname -r)/build
   PWD := $(shell pwd)
   default:
          $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

And finally build the module, insert it into the kernel, and prepare for the next step::

   make
   sudo insmod asoc_simple_card.ko
   cd ..


You should see a console output similar to this::

   pi@raspberrypi:~/snd_driver $ make
   make -C /lib/modules/4.4.13-v7+/build SUBDIRS=/home/pi/snd_driver modules
   make[1]: Entering directory '/home/pi/linux-e88b69aa792dbe2b289ff528c0d07303068aa0aa'
     CC [M]  /home/pi/snd_driver/asoc_simple_card.o
     Building modules, stage 2.
     MODPOST 1 modules
     CC      /home/pi/snd_driver/asoc_simple_card.mod.o
     LD [M]  /home/pi/snd_driver/asoc_simple_card.ko
   make[1]: Leaving directory '/home/pi/linux-e88b69aa792dbe2b289ff528c0d07303068aa0aa'

Next we build the loader which presents the device to the OS::

   mkdir loader
   cd loader

Copy the contents of the source from the forum post linked [#]_ below into a new file called ``loader.c``.

For reference, a copy of the relevant code from the post is included here::

   #include <linux/module.h>
   #include <linux/kernel.h>
   #include <linux/kmod.h>
   #include <linux/platform_device.h>
   #include <sound/simple_card.h>
   #include <linux/delay.h>
   /*
   modified for linux 4.1.5
   inspired by https://github.com/msperl/spi-config
   with thanks for https://github.com/notro/rpi-source/wiki
   as well as Florian Meier for the rpi i2s and dma drivers

   to use a differant (simple-card compatible) codec
   change the codec name string in two places and the
   codec_dai name string. (see codec's source file)

   fmt flags are set for vanilla i2s with rpi as clock slave

   N.B. playback vs capture is determined by the codec choice
    */

   void device_release_callback(struct device *dev) { /* do nothing */ };

   static struct asoc_simple_card_info snd_rpi_simple_card_info = {
      .card = "snd_rpi_simple_card", // -> snd_soc_card.name
      .name = "simple-card_codec_link", // -> snd_soc_dai_link.name
      .codec = "snd-soc-dummy", // -> snd_soc_dai_link.codec_name
      .platform = "3f203000.i2s",
      .daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFM,
      .cpu_dai = {
         .name = "3f203000.i2s", // -> snd_soc_dai_link.cpu_dai_name
         .sysclk = 0
      },
      .codec_dai = {
         .name = "snd-soc-dummy-dai", // -> snd_soc_dai_link.codec_dai_name
         .sysclk = 0
      },
   };
   static struct platform_device snd_rpi_simple_card_device = {
      .name = "asoc-simple-card", //module alias
      .id = 0,
      .num_resources = 0,
      .dev = {
         .release = &device_release_callback,
         .platform_data = &snd_rpi_simple_card_info, // *HACK ALERT*
      },
   };
   int hello_init(void)
   {
      const char *dmaengine = "bcm2708-dmaengine"; //module name
      int ret;

      ret = request_module(dmaengine);
      pr_alert("request module load '%s': %d\n",dmaengine, ret);
      ret = platform_device_register(&snd_rpi_simple_card_device);
      pr_alert("register platform device '%s': %d\n",snd_rpi_simple_card_device.name, ret);

      pr_alert("Hello World :)\n");
      return 0;
   }
   void hello_exit(void)
   {// you'll have to sudo modprobe -r the card & codec drivers manually (first?)
      platform_device_unregister(&snd_rpi_simple_card_device);
      pr_alert("Goodbye World!\n");
   }
   module_init(hello_init);
   module_exit(hello_exit);
   MODULE_DESCRIPTION("ASoC simple-card I2S setup");
   MODULE_AUTHOR("Plugh Plover");
   MODULE_LICENSE("GPL v2"); 


Make sure that line 21 is changes as shown below to ensure the Raspberry Pi 3 is the I2S master and the Microphone is the slave::

   .daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS,


.. [#] https://www.raspberrypi.org/forums/viewtopic.php?p=914713#p914713

Copy the below contents into ``Makefile`` again ensuring that the indent is a tab character rather than spaces::

   obj-m := loader.o
   KDIR := /lib/modules/$(shell uname -r)/build
   PWD := $(shell pwd)
   default:
          $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

And finally build the module and insert it into the kernel::

   make
   sudo insmod loader.ko
   cd ..


To verify that the I2S audio device driver module has been loaded and is accessible, you run the following command::

   arecord -l

This should generate a list of available ALSA captures devices something like this::

   **** List of CAPTURE Hardware Devices ****
   card 1: sndrpisimplecar [snd_rpi_simple_card], device 0: simple-card_codec_link snd-soc-dummy-dai-0 []
     Subdevices: 1/1
     Subdevice #0: subdevice #0

The physical sound card device presents a stereo I2S device to Linux. However, some Voice Assistant software such as Amazon's Alexa, requires access to a mono capture device. To present the stereo sound card as a mono device it is necessary to create a virtual mono capture device connected to the physical stereo capture device in ALSA. The microphone audio sent from the Microphone is a mono signal repeated on both stereo channels and so either channel will provide the required signal. The virtual mono capture device then needs to be set as the default device so that the client software picks it first when searching for input devices.

To setup the virtual capture device, modify the file ``.asoundrc`` found in the home directory as follows::

   pcm.monocard {
     type plug
     slave.pcm "plughw:1,0"
     slave.channels  2
     slave.rate 16000
   }

   ctl.monocard {
     type hw
     card 1
     device 0
   }

   pcm.!default {
      type asym
      playback.pcm {
         type hw
         card 0
      }
      capture.pcm {
         type plug
         slave.pcm "monocard"
      }
   }

   ctl.!default {
      type asym
      playback.pcm {
         type hw
         card 0
      }
      capture.pcm {
         type plug
         slave.pcm "monocard"
      }
   }

The above configuration keeps the default playback device as the 3.5mm jack on the Raspberry Pi however this can be changed to the I2S bus by changing the contents of for both ``pcm.!default {`` and ``ctl.!default {`` so that the ``playback.pcm {`` entries match ``capture.pcm``.

To apply the changes made in ``.asoundrc`` and enable the virtual capture device either reboot the Raspberry Pi or use the following command::

   sudo /etc/init.d/alsa-utils restart

Following the instructions given so far will enable the I2S soundcard in the current session. To make it available after reach reboot, it is necessary to load the modules at boot time. You can do this by adding a boot-time cron job to run the following script. Save this script as ``load_i2s_driver.sh`` in your home directory::

   cd ~
   sudo insmod snd_driver/asoc_simple_card.ko
   sudo insmod loader/loader.ko

The script can be auto-run at boot time by adding the following line in the editor window that appears after typing ``crontab -e``::

   @reboot /home/pi/load_i2s_driver.sh
Attachments
rpi_i2s_pinout.png
pinout
rpi_i2s_pinout.png (33.06 KiB) Viewed 6970 times

hedon77
Posts: 1
Joined: Mon Aug 22, 2016 12:31 pm

Re: I2S Success (at last) !

Mon Aug 22, 2016 12:53 pm

Hi,

You could verify my scheme of connections?

Thank you and best regards
Attachments
PCM1803ADB.png
Scheme PCM1803A RaspberryPi
PCM1803ADB.png (27.78 KiB) Viewed 6875 times

User avatar
Maash
Posts: 31
Joined: Tue Aug 23, 2016 10:18 am

Re: I2S Success (at last) !

Tue Aug 23, 2016 10:46 am

Hi all.


I've tried following (especially audio-badgers excellent guide) this to get external i2s microphone to work. However I'm totally clueless what comes to audio even tough I've got reasonably strong SW background.

The current system is such, that the I2S device is NXP board (TFA9880) that is controlled with pc software outputs 48kbit 2 channel I2S stuff without any RPI control (so no need to codec control). It also outputs the clocks so it should be the master (.daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFM).

I've used the carla's example (and I just tried the audio-badgers guide) and I can see the simple card as capture device. However when I record I only get random noise when played back with the aplay. I'm using the headphone output for playback and it should work (played another wav and it was OK). One problem point is that by default i2S left channel seems to be outputting something called coolflux output and right channel "current sense". I have no idea if this is OK or not.

And actually the external board stops transmitting, so I have a suspicion that somehow the RPI still outputs clocks to external device and it screws up the system.

*****************

As side note any good idiot's guide to alsa and audio formats(especially the i2s)? I'm pretty clueless even tough I've been through the alsa-project.org pages and read tons of alsa guides. My background is in graphics area, so this is quite different.
More like a comfyman...

User avatar
Maash
Posts: 31
Joined: Tue Aug 23, 2016 10:18 am

Re: I2S Success (at last) !

Tue Aug 23, 2016 11:22 am

Ok, with a little measuring with scope, the RPI really tries to drive both clock signals. Before capture is started, the NXP outputs really nice clocks on both channels. After starting the capture, the both lines go haywired.
More like a comfyman...

User avatar
Maash
Posts: 31
Joined: Tue Aug 23, 2016 10:18 am

Re: I2S Success (at last) !

Tue Aug 23, 2016 11:35 am

Ok, right when I decide finally to ask, I find the problem :D.

the daifmt clock config should be SND_SOC_DAIFMT_CBM_CFM, not SND_SOC_DAIFMT_CBS_CFM. It left the codec clock as slave (as defined in soc-dai.h under linux/sound/).

Now I can get some sort of recording, however huge amount of hiss and other ambient noice. The play speed is OK.
More like a comfyman...

User avatar
flatmax
Posts: 336
Joined: Thu May 26, 2016 10:36 pm

Re: I2S Success (at last) !

Tue Aug 23, 2016 12:00 pm

OK - you got rid of the sync issue ... which was probably due to LRCLK not making it through to the Pi ... assuming you are running codec master.

About the hiss ... can you get the LRCLK and ADC data lines up on a cro to inspect ?
Check that they look to be aligned ... if not, then you need to align them in a similar way to the audio injector sound card - check the snd_audioinjector_pi_soundcard_hw_params function here :
https://github.com/raspberrypi/linux/bl ... oundcard.c
The Ultra sound card - Live now on kickstarter : https://goo.gl/8HdqDi
Sound card for the Raspberry Pi with inbuilt microphone : http://www.audioinjector.net
Audio Inector Octo multitrack GPIO sound card : http://www.audioinjector.net

User avatar
Maash
Posts: 31
Joined: Tue Aug 23, 2016 10:18 am

Re: I2S Success (at last) !

Wed Aug 24, 2016 7:28 am

Ok, I tried changing the bclk, but it didn't really help. Actually it kept the hiss, but lost the audio.

However I noticed that the non-modified bclk plays little fast. like 10-15%.

From scope I checked that the bclk is exactly 44khz (44,09) and the another clock is 2,8mhz (2,823). 44 vs 48 would explain the speed difference. This is a little weird as the codec is configured to output 48khz stuff.
More like a comfyman...

User avatar
Maash
Posts: 31
Joined: Tue Aug 23, 2016 10:18 am

Re: I2S Success (at last) !

Wed Aug 24, 2016 9:17 am

Ok. The problem was the NXP SW that sampled with 48khz rate, but outputted at hardcoded 44.1khz rate. I changed the samplerate to 44.1, set the bclock conf from asoc_simple_card to 64 (44.1khz^-1/2.8mhz^-1) and recorded the stuff with 44.1khz rate.

Now there's basically no noise in the background.
More like a comfyman...

User avatar
Maash
Posts: 31
Joined: Tue Aug 23, 2016 10:18 am

Re: I2S Success (at last) !

Thu Aug 25, 2016 11:40 am

As separate note, how should these custom loaders be integrated to kernel if done properly? For testing purposes I'm currently loading them from my startup script as I haven't yet updated the kernel.

Modified simple_soundcard is straight enough, but should this loader format be changed to similar to fe bcm/hifiberry_dac.c with module_probe and few other additional functions on board?
More like a comfyman...

Return to “Interfacing (DSI, CSI, I2C, etc.)”

Who is online

Users browsing this forum: No registered users and 3 guests