This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM335x Multiple Audio Channels on MCASP0 Linux

Other Parts Discussed in Thread: PCM5102A

This probably isn't the place to ask this question, but I am not sure where is.  Feel free to point me somewhere else if there is a better place : ).  I am pretty new to linux audio and am having some trouble getting a grasp on it all.  

Basically i am using a WM8580 Codec on MCASP0 of the AM335x.  I have the codec driver all working and i can play audio and it comes out great on VOUT1 of the Codec.  The problem is when i try to route to the other channels.  The codec has three output channels VOUT1, VOUT2, and VOUT3.  I can't seem to get anywhere except VOUT1.  I put the following into my asound.conf file:

pcm.dmixed {
	type dmix
	ipc_key 1024
	slave {
		pcm "hw:0,0"
		rate 48000
		channels 4
	}
	bindings {
		0 0
		1 1
		2 2
		3 3
	}
}

pcm.frontx {
	type plug
	slave {
		pcm "dmixed"
		channels 4
	}
	ttable.0.0 1
	ttable.1.1 1
}

pcm.rearx {
	type plug
	slave {
		pcm "dmixed"
		channels 4
	}
	ttable.0.2 1
	ttable.1.3 1
}

pcm.bothx {
	type plug
	slave {
		pcm "dmixed"
		channels 4
	}
	ttable.0.0 1
	ttable.1.1 1
	ttable.0.2 1
	ttable.1.3 1
}

So what happens is when i just aplay straight to the card (aplay -D plughw:0,0 file.wav) it sounds great except it only comes out the VOUT1 channels.  When i try to play out say VOUT2 (aplay -D rearx file.wav) the sound comes out of VOUT1 still instead and is slower.  So it seems what is happening is basically when i up the channels from 2 to 4, the first 2 channels are transmitted at the first frame sync then it waits til the next frame sync and transmits channels 3&4.  This is creating my half speed sound on VOUT1 and resulting in no sound on VOUT2.

So any ideas of what the issue is?  Do i have something wrong in the asound.conf or maybe something that i don't have setup correctly as far as mcasp0 is concerned?

  • Nevermind, figured it out.  The problem was i had only set 2 TDM Slots in my board file in the kernel (arch/arm/mach-omap2/board-am335xevm.c).  This was the culprit:

    diff -r 9790044f6d7e arch/arm/mach-omap2/board-am335xevm.c
    --- a/arch/arm/mach-omap2/board-am335xevm.c	Fri Dec 13 14:48:46 2013 -0500
    +++ b/arch/arm/mach-omap2/board-am335xevm.c	Wed Jan 08 17:54:46 2014 -0500
    @@ -147,7 +155,7 @@
     	.rx_dma_offset	= 0x46000000,
     	.op_mode	= DAVINCI_MCASP_IIS_MODE,
     	.num_serializer	= ARRAY_SIZE(qrs_iis_serializer_direction0),
    -	.tdm_slots	= 2,
    +	.tdm_slots	= 4,
     	.serial_dir	= qrs_iis_serializer_direction0,
     	.asp_chan_q	= EVENTQ_2,
     	.version	= MCASP_VERSION_3,
    @@ -211,7 +219,7 @@
     };

    I also changed this in sound/soc/davinci/davinci-mcasp.c (although i don't know if it has any bearing on the actual channel limit of mcasp0 or not):

     diff -r 9790044f6d7e sound/soc/davinci/davinci-mcasp.c
    --- a/sound/soc/davinci/davinci-mcasp.c	Fri Dec 13 14:48:46 2013 -0500
    +++ b/sound/soc/davinci/davinci-mcasp.c	Wed Jan 08 17:54:46 2014 -0500
    @@ -881,7 +881,7 @@
     		.name		= "davinci-mcasp.0",
     		.playback	= {
     			.channels_min	= 2,
    -			.channels_max 	= 2,
    +			.channels_max 	= 4,
     			.rates 		= DAVINCI_MCASP_RATES,
     			.formats	= DAVINCI_MCASP_PCM_FMTS,
     		},

    I can now work VOUT1 and VOUT2 : )  i am going to up these to 6 and see if i can't get all of them going now!

    I swear half the time i post something i figure it out right after... doh!

  • Thanks for updating the thread Jarrod. This can help other people who face similar issues.

  • No problem!  This forum has helped me immensely, so I would be glad if i could help others : ).

    I may have spoke to soon though i don't know i have completely resolved everything.  First of all it definitely is necessary to change max_channels in davinci-mcasp.c.  I will get a segmentation fault immediately when trying to play audio if not changed.  I am not sure what effect changing this value has and why it was only set to 2 to begin with???

    So i had four channels working good and decided to go to 6.  To do this i upped both the number of TDM slots and the max_channels to 6.  When i route the audio to all channels so that i am playing the audio out of VOUT1, VOUT2, and VOUT3 all sounds good.  However when i route the audio to only some of the outputs that is where i hit trouble...

    So for example i try to play audio out of just VOUT2:

    pcm.rearx {
        type plug
        slave {
            pcm "dmixed"
            channels 6
        }
        ttable.0.2 1
        ttable.1.3 1
    }

    What happens is the audio is the correct speed but all garbled.  When i look at it on the scope i can see that the audio is not always transmitting in the correct slot.  It seems to skip all over!  It will transmit in slot 1/2, 3/4 and 5/6 only one set per frame though not all at once.  

    It seems as though it transfers alot of frames in one slot then all the sudden moves to the next slot and transfers in that slot for a bunch of frames and continues to switch like this.  The left/right alignment is always correct meaning it jumps slots in 2's.

    Not sure what is happening here, maybe something to do with buffers or a side effect of changing the max_channels setting?

  • Alright i seemed to have arrived at a solution.  I tried out several things and this wiki:

    http://processors.wiki.ti.com/index.php/McBSP_Channel_Swapping

    made me think that the dma was having issues.  So i changed the WNUMEVT value in the WFIFOCTL register of the MCASP module to a lower value in hopes that it would fire a signal sooner and allow things to keep up.  Since i have 1 transmitter enabled it has to be a multiple of 1 according to the datasheet so currently i just have it set to 1.  Here is my current config setup:

    diff -r 9790044f6d7e arch/arm/mach-omap2/board-am335xevm.c
    --- a/arch/arm/mach-omap2/board-am335xevm.c	Fri Dec 13 14:48:46 2013 -0500
    +++ b/arch/arm/mach-omap2/board-am335xevm.c	Thu Jan 09 14:09:02 2014 -0500
    @@ -147,12 +155,12 @@
     	.rx_dma_offset	= 0x46000000,
     	.op_mode	= DAVINCI_MCASP_IIS_MODE,
     	.num_serializer	= ARRAY_SIZE(am335x_iis_serializer_direction0),
    -	.tdm_slots	= 2,
    +	.tdm_slots	= 6,
     	.serial_dir	= am335x_iis_serializer_direction0,
     	.asp_chan_q	= EVENTQ_2,
     	.version	= MCASP_VERSION_3,
    -	.txnumevt	= 32,
    -	.rxnumevt	= 32,
    +	.txnumevt	= 1,
    +	.rxnumevt	= 1,
     	.get_context_loss_count	= omap_pm_get_dev_context_loss_count,
     };

    Also, had to update the max channels:

    diff -r 9790044f6d7e sound/soc/davinci/davinci-mcasp.c
    --- a/sound/soc/davinci/davinci-mcasp.c	Fri Dec 13 14:48:46 2013 -0500
    +++ b/sound/soc/davinci/davinci-mcasp.c	Thu Jan 09 14:19:42 2014 -0500
    @@ -881,7 +881,7 @@
     		.name		= "davinci-mcasp.0",
     		.playback	= {
     			.channels_min	= 2,
    -			.channels_max 	= 2,
    +			.channels_max 	= 6,
     			.rates 		= DAVINCI_MCASP_RATES,
     			.formats	= DAVINCI_MCASP_PCM_FMTS,
     		},

    All the audio sounds great from all 6 channels now!  As to why this corrected the issue i can't be completely sure, and if was because the DMA was struggling to keep up I'm wondering if i will encounter issues later on when i have more things happening at once...?

    If anyone has suggestions as to what the issue most likely was and why this corrected it i would definitely like to hear it!  I would like to be sure that things will run solid even when the system gets busy with other things : ).

  • I also would like to do a project with multiple audio outputs. At the moment I have only one audio output through PCM5102a dac. Actually my intension was to increase the number of DACs and have a multi channel audio board but I am stuck at doing that. However, using a multichannel dac as you did is also ok. I have beaglebone white and sitara sdk 7.0 kernel running on that.

    Jarrod , did  you have to change your device tree file and which driver did you use for your codec? What sound devices do you have when you type "aplay -l" after all these changes you did? It would be very nice if you can help since I am stuck with multiple DACs approach.

    I followed the following guide to get audio from my beaglebone white:

    http://processors.wiki.ti.com/index.php/Sitara_Linux_SDK_Audio_DAC_Example

  • Hello,

    I had done this project with the older TI SDK (6.00.00), which had kernel version 3.2 meaning no device tree. I just used the WM8580 driver for my codec because that is the codec i used which is basically a code with 3 DACs, an ADC, and an S/PDIF.

    I had to setup MCASP0 to have 6 slots since that codec has 3 stereo outputs. I don't know if i'll be a great help because this was my first time fighting through audio as well but i'll do what i can to help! Here is the info you requested:

    Here is my aplay -l output:

    **** List of PLAYBACK Hardware Devices ****
    card 0: WM8580 [EVM WM8580], device 0: Playback wm8580-hifi-playback-0 []
    Subdevices: 1/1
    Subdevice #0: subdevice #0

    I believe alot of this is pasted in peices above, but basically i made the following mods to the 3.2 SDK Kernel:

    Changed sound/soc/davinci/davinci-mcasp.c to increase the max slots:

    @@ -881,7 +881,7 @@
     		.name		= "davinci-mcasp.0",
     		.playback	= {
     			.channels_min	= 2,
    -			.channels_max 	= 2,
    +			.channels_max 	= 6,
     			.rates 		= DAVINCI_MCASP_RATES,
     			.formats	= DAVINCI_MCASP_PCM_FMTS,
     		},

    Changed TDM slots in my board config file to match the number i have (arch/arm/mach-omap2/board-am335xevm.c):

    @@ -147,12 +155,12 @@
     	.rx_dma_offset	= 0x46000000,
     	.op_mode	= DAVINCI_MCASP_IIS_MODE,
     	.num_serializer	= ARRAY_SIZE(qrs_iis_serializer_direction0),
    -	.tdm_slots	= 2,
    +	.tdm_slots	= 6,
     	.serial_dir	= qrs_iis_serializer_direction0,
     	.asp_chan_q	= EVENTQ_2,
     	.version	= MCASP_VERSION_3,
    -	.txnumevt	= 32,
    -	.rxnumevt	= 32,
    +	.txnumevt	= 1,
    +	.rxnumevt	= 1,
     	.get_context_loss_count	= omap_pm_get_dev_context_loss_count,
     };

    I modified sound/soc/davinci/davinci-evm.c alot to correctly setup my codec and ended up with this:

    /*
     * ASoC driver for TI DAVINCI EVM platform
     *
     * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
     * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.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.
     */
    //Includes
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/timer.h>
    #include <linux/interrupt.h>
    #include <linux/platform_device.h>
    #include <linux/i2c.h>
    #include <sound/core.h>
    #include <sound/pcm.h>
    #include <sound/soc.h>
    #include <sound/pcm_params.h>
    
    #include <asm/dma.h>
    #include <asm/mach-types.h>
    
    #include <asm/hardware/asp.h>
    #include <mach/edma.h>
    #include <mach/board-am335xevm.h>
    
    #include "../codecs/wm8580.h"
    #include "davinci-pcm.h"
    #include "davinci-i2s.h"
    #include "davinci-mcasp.h"
    
    //Defines
    /* Board has a 12MHZ crystal attached to WM8580 */
    #define EVM_WM8580_FREQ 12000000		//!< Board has a 12MHz crystal attached to WM8580
    #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)	//!< MCASP Communication format, codec = master, mode = DSP Mode B
    
    //Enumerations and Structs
    /*!
    * Defines the different interfaces available on the codec
    */
    enum {
    	PRI_PLAYBACK = 0,	//!< Playback over the primary interface
    	PRI_CAPTURE,		//!< Capture over the primary interface
    	SEC_PLAYBACK,		//!< Playback over the secondary interface (not used)
    };
    
    /*!
    * Define the different output interfaces of the card (3 out 1 in)
    */
    static const struct snd_soc_dapm_widget evm_wm8580_dapm_widgets[] = {
    	SND_SOC_DAPM_HP("Front", NULL),					//!< Front Speakers (VOUT1)
    	SND_SOC_DAPM_HP("Center+Sub", NULL),			//!< Center and Sub Speakers (VOUT2)
    	SND_SOC_DAPM_HP("Rear", NULL),					//!< Rear Speakers (VOUT3)
    
    	SND_SOC_DAPM_MIC("MicIn", NULL),				//!< Microphone In (AINL)
    	SND_SOC_DAPM_LINE("LineIn", NULL),				//!< Line In (AIN)
    };
    
    /*!
    * Define all the connection routes for the different interfaces
    */
    static const struct snd_soc_dapm_route evm_wm8580_audio_map[] = {
    	{"AINL", NULL, "MicIn"},			//!< MicIn feeds AINL
    
    	{"AINL", NULL, "LineIn"},			//!< LineIn feeds AINL
    	{"AINR", NULL, "LineIn"},			//!< LineIn feeds AINR
    
    	{"Front", NULL, "VOUT1L"},			//!< Front Left is fed by VOUT1L
    	{"Front", NULL, "VOUT1R"},			//!< Front Right is fed VOUT1R
    
    	{"Center+Sub", NULL, "VOUT2L"},		//!< Center is fed by VOUT2L
    	{"Center+Sub", NULL, "VOUT2R"},		//!< Sub is fed by VOUT2R
    
    	{"Rear", NULL, "VOUT3L"},			//!< Rear Left is fed by VOUT3L
    	{"Rear", NULL, "VOUT3R"},			//!< Rear Right is fed by VOUT3R
    };
    
    //Local Variables
    static struct platform_device *evm_snd_device;		//!< For storing a reference to the device so it can be closed on exit
    
    //Functions+
    /*!
    * @name evm_hw_params
    * @brief Setup the parameters for playing the requested track
    *
    * Called before playback/capture is started to setup the peices to
    * correctly perform the requested operations.
    *
    * @param substream the stream to play/capture the data on
    * @param params the parameters for use in playback/capture
    *
    * @returns 0 returned if succesful, otherwise error code
    */
    static int evm_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 *cpu_dai = rtd->cpu_dai;
    	struct snd_soc_dai *codec_dai = rtd->codec_dai;
    	unsigned int pll_out;
    	int rfs;
    	int ret;
    
    	/* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
    	 * This criterion can't be met if we request PLL output
    	 * as {8000x256, 64000x256, 11025x256}Hz.
    	 * As a wayout, we rather change rfs to a minimum value that
    	 * results in (params_rate(params) * rfs), and itself, acceptable
    	 * to both - the CODEC and the CPU.
    	 */
    	switch (params_rate(params)) {
    	case 16000:
    	case 22050:
    	case 32000:
    	case 44100:
    	case 48000:
    	case 88200:
    	case 96000:
    		rfs = 256;
    		break;
    	case 64000:
    		rfs = 384;
    		break;
    	case 8000:
    	case 11025:
    		rfs = 512;
    		break;
    	default:
    		return -EINVAL;
    	}
    	//Calculate pll_out value
    	pll_out = params_rate(params) * rfs;
    
    	// Set the Codec DAI configuration 
    	ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
    	if (ret < 0)
    		return ret;
    
    	// Set the AP DAI configuration 
    	ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
    	if (ret < 0)
    		return ret;
    
    	// Set WM8580 to drive MCLK from its PLLA 
    	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, WM8580_CLKSRC_PLLA);
    	if (ret < 0)
    		return ret;
    
    	// Make nothing drive the clkout pin 
    	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_CLKOUTSRC, WM8580_CLKSRC_NONE);
    	if (ret < 0)
    		return ret;
    
    	//Set the Codec's PLL
    	ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, EVM_WM8580_FREQ, pll_out);
    	if (ret < 0)
    		return ret;
    
    	//Set the codecs system clock
    	ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA, pll_out, SND_SOC_CLOCK_IN);
    	if (ret < 0)
    		return ret;
    
    	return 0;
    }
    
    /*!
    * @name evm_wm8580_init_paiftx
    * @brief Initializes capturing by enabling/disabling the correct pins
    *
    * Called to setup the card for capturing audio.  Basically just disables
    * the mic since there isn't one hooked in.
    *
    * @param rtd The current runtime
    *
    * @returns 0 returned if succesful, otherwise error code
    */
    static int evm_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd)
    {
    	struct snd_soc_codec *codec = rtd->codec;
    	struct snd_soc_dapm_context *dapm = &codec->dapm;
    
    	/* Enabling the microphone requires the connecting of
    	 * and actual microphone jack to audio in left.
    	 */
    	snd_soc_dapm_disable_pin(dapm, "MicIn");
    
    	return 0;
    }
    
    /*!
    * Define the operations for the audio card (hw params setup)
    */
    static struct snd_soc_ops evm_ops = {
    	.hw_params = evm_hw_params,			//!< Function to setup hardware for audio format
    };
    
    /*!
    * Defines the different interfaces available on the card
    */
    static struct snd_soc_dai_link evm_dai[] = {
    	[PRI_PLAYBACK] = { /* Primary Playback i/f */
    		.name = "WM8580 PAIF RX",
    		.stream_name = "Playback",
    		.cpu_dai_name = "davinci-mcasp.0",
    		.codec_dai_name = "wm8580-hifi-playback",
    		.platform_name = "davinci-pcm-audio",
    		.codec_name = "wm8580.1-001b",
    		.ops = &evm_ops,
    	},
    	[PRI_CAPTURE] = { /* Primary Capture i/f */
    		.name = "WM8580 PAIF TX",
    		.stream_name = "Capture",
    		.cpu_dai_name = "davinci-mcasp.0",
    		.codec_dai_name = "wm8580-hifi-capture",
    		.platform_name = "davinci-pcm-audio",
    		.codec_name = "wm8580.1-001b",
    		.init = evm_wm8580_init_paiftx,
    		.ops = &evm_ops,
    	},
    	[SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */
    		.name = "Sec_FIFO TX",
    		.stream_name = "Playback",
    		.cpu_dai_name = "davinci-mcasp.0",
    		.codec_dai_name = "wm8580-hifi-playback",
    		.platform_name = "davinci-pcm-audio",
    		.codec_name = "wm8580.1-001b",
    		.ops = &evm_ops,
    	},
    };
    
    /*!
    * Define the card, its available interfaces, and its mapping
    */
    static struct snd_soc_card evm_snd_soc_card = {
    	.name = "EVM WM8580",
    	.dai_link = evm_dai,
    	.num_links = 2,
    
    	.dapm_widgets = evm_wm8580_dapm_widgets,
    	.num_dapm_widgets = ARRAY_SIZE(evm_wm8580_dapm_widgets),
    	.dapm_routes = evm_wm8580_audio_map,
    	.num_dapm_routes = ARRAY_SIZE(evm_wm8580_audio_map),
    };
    
    /*!
    * @name evm_init
    * @brief Initializes the sound device
    *
    * Called when module is loaded to initialize the audio interface
    * for use.
    *
    * @returns 0 returned if succesful, otherwise error code
    */
    static int __init evm_init(void)
    {
    	int ret;
    
    	evm_snd_device = platform_device_alloc("soc-audio", -1);
    	if (!evm_snd_device)
    		return -ENOMEM;
    
    	platform_set_drvdata(evm_snd_device, &evm_snd_soc_card);
    	ret = platform_device_add(evm_snd_device);
    	if (ret)
    		platform_device_put(evm_snd_device);
    
    	return ret;
    }
    
    /*!
    * @name evm_exit
    * @brief DeInitializes the sound device
    *
    * Called when module is unloaded to unregister the
    * audio interface from further use.
    *
    * @returns 0 returned if succesful, otherwise error code
    */
    static void __exit evm_exit(void)
    {
    	platform_device_unregister(evm_snd_device);
    }
    
    //REGISTER THE MODULE
    module_init(evm_init);
    module_exit(evm_exit);
    
    MODULE_AUTHOR("Vladimir Barinov");
    MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver");
    MODULE_LICENSE("GPL");

    Then i just had to setup my asound.conf file to be able to route audio to the channels i wanted with alsasink and i was good to go : ). Let me know if there is any other info i can give that would be helpful!!!

  • Jarrod,

    Thanks a lot for the info--I am going to add this to the Sitara Audio Wiki.  That is interesting that decreasing the fifo depth to 1 fixed your issue.  Maybe it was a ALSA period size misalignment issue? Look at this page for details.

    Emrah,

    Were you able to get the PCM5102a working based on the example?  Can you give me details on how you will be connecting your audio hardware to produce multiple outputs?  For instance, will you be using both McASPs?  What will be the clocking configuration?  How many TDM slots do you plan on having?

    Regards,

    Josh

  • Hmmm, interesting.  That does sound like what i was experiencing.  I am not able to test right now but i will try changing that stuff in the near future and see if i notice a difference.  I will post back results after i try i it out.  Thanks!