AM335x Audio Driver's Guide

= Introduction =

The AIC31 audio module contains audio analog inputs and outputs. It is connected to the main AM335x processor through the TDM/I2S interface (audio interface) and used to transmit and receive audio data. The AIC31 audio codec is connected via Multi-Channel Audio Serial Port (McASP) interface, a communication peripheral, to the main processor.

McASP provides a full-duplex direct serial interface between the host device (AM335x processor) and other audio devices in the system such as the AIC31 codec. It provides a direct interface to industry standard codecs, analog interface chips (AICs) and other serially connected A/D and D/A devices:


 * Inter-IC Sound (I2S) compliant devices
 * Pulse Code Modulation (PCM) devices
 * Time Division Multiplexed (TDM) bus devices.

The AIC31 audio module is controlled by internal registers that can be accessed by the high speed I2C control interface.

This user manual defines and describes the usage of user level and platform level interfaces of the ALSA SoC Audio driver.

Features
This section describes the features supported by ALSA SoC Audio driver.


 * Supports AIC31 audio codec on AM335x in ALSA SoC framework.
 * Multiple sample rates support (8KHz, 16KHz, 22.05KHz, 32KHz, 44.1KHz, 48KHz, 64KHz, 88.2KHz and 96KHz) for both capture and playback.
 * Supports audio in stereo mode.
 * Supports simultaneous playback and record (full-duplex mode).
 * Start, stop, pause and resume feature.
 * Supports mixer interface for audio codecs.
 * Supports MMAP mode for both playback and capture. (verify)
 * McASP is configured as slave and AIC31 Codec is configured as master.
 * There are 2 instances of McASP on AM335x SOC
 * Audio Codecs are available in 2 EVM Configs.
 * McASP1 is used for Profile 0, 3 &amp; 7 on AM335x General Purpose EVM.
 * McASP1 instance is used on AM335x IP-Phone EVM.
 * For more details about EVM Configurations &amp; Profile Descriptions, Please refer to EVM reference manual.

= ALSA SoC Architecture =

Introduction
The overall project goal of the ALSA System on Chip (ASoC) layer is to provide better ALSA support for embedded system on chip processors and portable audio codecs. Currently there is some support in the kernel for SoC audio, however it has some limitations:


 * Currently, codec drivers are often tightly coupled to the underlying SoC cpu. This is not really ideal and leads to code duplication.
 * There is no standard method to signal user initiated audio events e.g. Headphone/Mic insertion, Headphone/Mic detection after an insertion event.
 * Current drivers tend to power up the entire codec when playing (or recording) audio. This is fine for a PC, but tends to waste a lot of power on portable devices. There is also no support for saving power via changing codec oversampling rates, bias currents, etc.

Design
The ASoC layer is designed to address these issues and provide the following features:


 * Codec independence: Allows reuse of codec drivers on other platforms and machines.
 * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC interface and codec registers it's audio interface capabilities with the core and are subsequently matched and configured when the application hw params are known.
 * Dynamic Audio Power Management (DAPM): DAPM automatically sets the codec to it's minimum power state at all times. This includes powering up/down internal power blocks depending on the internal codec audio routing and any active streams.
 * Pop and click reduction: Pops and clicks can be reduced by powering the codec up/down in the correct sequence (including using digital mute). ASoC signals the codec when to change power states.

To achieve all this, ASoC splits an embedded audio system into three components:


 * Codec driver: The codec driver is platform independent and contains audio controls, audio interface capabilities, codec dapm definition and codec IO functions.
 * Platform driver: The platform driver contains the audio dma engine and audio interface drivers (e.g. I2S, AC97, PCM) for that platform.
 * Machine driver: The machine driver handles any machine specific controls and audio events i.e. turning on an amp at start of playback.

Following architecture diagram shows all the components and the interactions among them:

AM335x :



= Driver Configuration =

To enable/disable audio support, start the Linux Kernel Configuration tool:

$ make CROSS_COMPILE=arm-arago-linux-gnueabi- ARCH=arm menuconfig Select Device Drivers from the main menu. ...   ...    Power management options ---&gt; [ ] Networking support ---&gt; Device Drivers ---&gt; File systems ---&gt; Kernel hacking ---&gt; ...   ...

Select Sound card support as shown here: ...   ...    Multimedia devices ---&gt; Graphics support ---&gt; &lt;*&gt; Sound card support ---&gt; [*] HID Devices ---&gt; [*] USB support ---&gt; ...   ...

Select Advanced Linux Sound Architecture as shown here:

--- Sound card support &lt;*&gt; Advanced Linux Sound Architecture ---&gt; &lt; &gt; Open Sound System (DEPRECATED) ---&gt;

Select ALSA for SoC audio support as shown here:

...   ... [*] ARM sound devices ---&gt; [*] SPI sound devices ---&gt; &lt;*&gt; ALSA for SoC audio support ---&gt;

For AM335x, select SoC Audio for AM33XX chip as shown here: --- ALSA for SoC audio support &lt;*&gt; SoC Audio for the AM33XX chip &lt;*&gt; SoC Audio support for AM335X EVM &lt; &gt; Build all ASoC CODEC drivers (NEW)

Note: Soc Audio support for AM335X EVM option appears iff SoC Audio for the AM33XX chip is selected

Module Build
Module build for the audio driver is supported. To do this, at all the places mentioned in the section above select module build (short-cut key M).

= Application Interface =

Module build for the audio driver is supported. To do this, at all the places mentioned in the section above select module build (short-cut key M).

After the modules are built, the following sound related kernel modules (*.ko) will be generated

sound/soundcore.ko   sound/core/snd.ko   sound/core/snd-hwdep.ko    sound/core/snd-timer.ko    sound/core/snd-page-alloc.ko    sound/core/snd-pcm.ko    sound/core/snd-rawmidi.ko    sound/core/snd-usb-audio.ko    sound/core/snd-usb-lib.ko    sound/soc/snd-soc-core.ko    sound/soc/codecs/snd-soc-tlv320aic3x.ko   sound/soc/davinci/snd-soc-davinci.ko   sound/soc/davinci/snd-soc-davinci-mcasp.ko   sound/soc/davinci/snd-soc-evm.ko

The kernel modules need to be inserted in the order listed above.

Application developer uses ALSA-lib, a user space library, rather than the kernel API. The library offers 100% of the functionality of the kernel API, but adds major improvements in usability, making the application code simpler and better looking.

The online-documentation for the same is available at: ALSA Project

Amixer commands
To unmute/mute the microphone input

$ amixer sset ‘Right PGA Mixer Mic3L’ on/off $ amixer sset ‘Right PGA Mixer Mic3R’ on/off $ amixer sset ‘Left PGA Mixer Mic3L’ on/off $ amixer sset ‘Left PGA Mixer Mic3R’ on/off

To unmute/mute the line input and use differential configuration

$ amixer sset 'Right PGA Mixer Line1R' on/off $ amixer sset 'Right PGA Mixer Line1L' on/off $ amixer sset 'Left PGA Mixer Line1R' on/off $ amixer sset 'Left PGA Mixer Line1L' on/off $ amixer sset 'Left Line1R Mux' differential $ amixer sset 'Right Line1L Mux' differential

To increase the volume for capture and playback

$ amixer cset name='PCM Playback Volume' x%,x% (x between 0-100) $ amixer cset name='PGA Capture Volume' x%,x%

amixer cset name='PGA Capture Volume' 30%,30%

Device Interface
The operational interface in  contains three main types of devices:


 * PCM devices for recording or playing digitized sound samples,
 * CTL devices that allow manipulating the internal mixer and routing of the card, and,
 * MIDI devices to control the MIDI port of the card, if any.

Proc Interface
The  kernel interface is a status and configuration interface. A lot of useful information about the sound system can be found in the  subdirectory.

See the table below for different proc entries in :

Commonly Used APIs
Some of the commonly used APIs to write an ALSA based application are:

User Space Interactions
This section depicts the sequence of operations for a simple playback and capture application.



= Sample Applications =

This chapter describes the audio sample applications provided along with the package. The source for these sample applications are available in the Examples directory of the Release Package folder.

Introduction
Writing an audio application involves the following steps:


 * Opening the audio device
 * Set the parameters of the device
 * Receive audio data from the device or deliver audio data to the device
 * Close the device

These steps are explained in detail in this section.  Note User space ALSA libraries can be downloaded from this link ALSA Project Download. User needs to build and install them before he starts using the ALSA based applications.

A minimal playback application
This program opens an audio interface for playback, configures it for stereo, 16 bit, 44.1kHz, interleaved conventional read/write access. Then its delivers a chunk of random data to it, and exits. It represents about the simplest possible use of the ALSA Audio API, and isn't meant to be a real program.

Opening the audio device
To write a simple PCM application for ALSA, we first need a handle for the PCM device. Then we have to specify the direction of the PCM stream, which can be either playback or capture. We also have to provide some information about the configuration we would like to use, like buffer size, sample rate, pcm data format. So, first we declare:


 * 1) include &lt;stdio.h&gt;
 * 2) include &lt;stdlib.h&gt;
 * 3) include &lt;alsa/asoundlib.h&gt;


 * 1) define BUFF_SIZE 4096

int main (int argc, char *argv[]) { int err; short buf[BUFF_SIZE]; int rate = 44100; /* Sample rate */ unsigned int exact_rate; /* Sample rate returned by */ /* Handle for the PCM device */ snd_pcm_t *playback_handle; /* Playback stream */ snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; /* This structure contains information about the hardware and can be used to specify the configuration to be used for */ /* the PCM stream. */ snd_pcm_hw_params_t *hw_params;

The most important ALSA interfaces to the PCM devices are the "plughw" and the "hw" interface. If you use the "plughw" interface, you need not care much about the sound hardware. If your sound card does not support the sample rate or sample format you specify, your data will be automatically converted. This also applies to the access type and the number of channels. With the "hw" interface, you have to check whether your hardware supports the configuration you would like to use. Otherwise, user can use the default interface for playback by:

/* Name of the PCM device, like plughw:0,0 */ /* The first number is the number of the soundcard, the second number is the number of the device. */

static char *device = "default"; /* playback device */

Now we can open the PCM device:

/* Open PCM. The last parameter of this function is the mode. */ if ((err = snd_pcm_open (&amp;playback_handle, device, stream, 0)) &lt; 0) { fprintf (stderr, "cannot open audio device (%s)\n", snd_strerror (err)); exit (1); }

Setting the parameters of the device
Now we initialize the variables and allocate the  structure:

/* Allocate the snd_pcm_hw_params_t structure on the stack. */ if ((err = snd_pcm_hw_params_malloc (&amp;hw_params)) &lt; 0) { fprintf (stderr, "cannot allocate hardware parameters (%s)\n", snd_strerror (err)); exit (1); }

Before we can write PCM data to the soundcard, we have to specify access type, sample format, sample rate, number of channels, number of periods and period size. First, we initialize the hwparams structure with the full configuration space of the soundcard:

/* Init hwparams with full configuration space */ if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) &lt; 0) { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)); exit (1); }

Now configure the desired parameters. For this example, we assume that the soundcard can be configured for stereo playback of 16 Bit Little Endian data, sampled at 44100 Hz. Therefore, we restrict the configuration space to match this configuration only. The access type specifies the way in which multi-channel data is stored in the buffer. For INTERLEAVED access, each frame in the buffer contains the consecutive sample data for the channels. For 16 Bit stereo data, this means that the buffer contains alternating words of sample data for the left and right channel.

/* Set access type. */ if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) &lt; 0) { fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)); exit (1); }

/* Set sample format */ if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) &lt; 0) { fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err)); exit (1); }

/* Set sample rate. If the exact rate is not supported by the hardware, use nearest possible rate. */ exact_rate = rate; if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, &amp;exact_rate, 0)) &lt; 0) { fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)); exit (1); }

if (rate != exact_rate) { fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n ==&gt; Using %d Hz instead.\n", rate, exact_rate); }

/* Set number of channels */ if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 2)) &lt; 0) { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)); exit (1); }

Now we apply the configuration to the PCM device pointed to by  and prepare the PCM device. /* Apply HW parameter settings to PCM device and prepare device. */ if ((err = snd_pcm_hw_params (playback_handle, hw_params)) &lt; 0) { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)); exit (1); }

snd_pcm_hw_params_free (hw_params);

if ((err = snd_pcm_prepare (playback_handle)) &lt; 0) { fprintf (stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror (err)); exit (1); }

Writing data to the device
After the PCM device is configured, we can start writing PCM data to it. The first write access will start the PCM playback. For interleaved write access, we use the function:

/* Write some junk data to produce sound. */ if ((err = snd_pcm_writei (playback_handle, buf, BUFF_SIZE/2)) != BUFF_SIZE/2) { fprintf (stderr, "write to audio interface failed (%s)\n", snd_strerror (err)); exit (1); } else { fprintf (stdout, "snd_pcm_writei successful\n"); }

After the PCM playback is started, we have to make sure that our application sends enough data to the soundcard buffer. Otherwise, a buffer under-run will occur. After such an under-run has occurred,  should be called.

Closing the device
After the data has been transferred, the device needs to be closed by calling: snd_pcm_close (playback_handle);

exit (0); }

A minimal record application
This program opens an audio interface for capture, configures it for stereo, 16 bit, 44.1kHz, interleaved conventional read/write access. Then its reads a chunk of random data from it, and exits. It isn't meant to be a real program.

Note that it is not possible to use one pcm handle for both playback and capture. So you have to configure two handles if you want to access the PCM device in both directions.


 * 1) include &lt;stdio.h&gt;
 * 2) include &lt;stdlib.h&gt;
 * 3) include &lt;alsa/asoundlib.h&gt;


 * 1) define BUFF_SIZE 4096

int main (int argc, char *argv[]) { int err; short buf[BUFF_SIZE]; int rate = 44100; /* Sample rate */ int exact_rate; /* Sample rate returned by */ snd_pcm_t *capture_handle;

/* This structure contains information about the hardware and can be used to specify the configuration to be used for */ /* the PCM stream. */ snd_pcm_hw_params_t *hw_params;

/* Name of the PCM device, like hw:0,0 */ /* The first number is the number of the soundcard, the second number is the number of the device. */ static char *device = "hw:0,0"; /* capture device */

/* Open PCM. The last parameter of this function is the mode. */ if ((err = snd_pcm_open (&amp;capture_handle, device, SND_PCM_STREAM_CAPTURE, 0)) &lt; 0) { fprintf (stderr, "cannot open audio device (%s)\n", snd_strerror (err)); exit (1); }

memset(buf,0,BUFF_SIZE);

/* Allocate the snd_pcm_hw_params_t structure on the stack. */ if ((err = snd_pcm_hw_params_malloc (&amp;hw_params)) &lt; 0) { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)); exit (1); }

/* Init hwparams with full configuration space */ if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) &lt; 0) { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)); exit (1); }

/* Set access type. */ if ((err = snd_pcm_hw_params_set_access (capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) &lt; 0) { fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)); exit (1); }

/* Set sample format */ if ((err = snd_pcm_hw_params_set_format (capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) &lt; 0) { fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err)); exit (1); }

/* Set sample rate. If the exact rate is not supported by the hardware, use nearest possible rate. */ exact_rate = rate; if ((err = snd_pcm_hw_params_set_rate_near (capture_handle, hw_params, &amp;exact_rate, 0)) &lt; 0) { fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)); exit (1); }

if (rate != exact_rate) { fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n ==&gt; Using %d Hz instead.\n", rate, exact_rate); }

/* Set number of channels */ if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, 2)) &lt; 0) { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)); exit (1); }

/* Apply HW parameter settings to PCM device and prepare device. */ if ((err = snd_pcm_hw_params (capture_handle, hw_params)) &lt; 0) { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)); exit (1); }

snd_pcm_hw_params_free (hw_params);

if ((err = snd_pcm_prepare (capture_handle)) &lt; 0) { fprintf (stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror (err)); exit (1); }

/* Read data into the buffer. */ if ((err = snd_pcm_readi (capture_handle, buf, 128)) != 128) { fprintf (stderr, "read from audio interface failed (%s)\n", snd_strerror (err)); exit (1); } else { fprintf (stdout, "snd_pcm_readi successful\n"); }

snd_pcm_close (capture_handle);

exit (0); }