world leader in high performance signal processing
This version (16 Dec 2014 09:56) was approved by mhennerich.The Previously approved version (27 Nov 2014 14:36) is available.Diff

ADAU1977 Sound CODEC Linux Driver

Supported Devices

Evaluation Boards

Source Code

Status

Source Mainlined?
git Yes

Files

Example device initialization

For compile time configuration, it’s common Linux practice to keep board- and application-specific configuration out of the main driver file, instead putting it into the board support file.

For devices on custom boards, as typical of embedded and SoC-(system-on-chip) based hardware, Linux uses platform_data to point to board-specific structures describing devices and how they are connected to the SoC. This can include available ports, chip variants, preferred modes, default initialization, additional pin roles, and so on. This shrinks the board-support packages (BSPs) and minimizes board and application specific #ifdefs in drivers.

21 Oct 2010 16:10 · Michael Hennerich

The platform data for the ADAU1977 allow to specify the MICBIAS pin voltage. If no platform data is specified (i.e. set to NULL) the default voltage of 8.5 V will be used.

/**
 * enum adau1977_micbias - ADAU1977 MICBIAS pin voltage setting
 * @ADAU1977_MICBIAS_5V0: MICBIAS is set to 5.0 V
 * @ADAU1977_MICBIAS_5V5: MICBIAS is set to 5.5 V
 * @ADAU1977_MICBIAS_6V0: MICBIAS is set to 6.0 V
 * @ADAU1977_MICBIAS_6V5: MICBIAS is set to 6.5 V
 * @ADAU1977_MICBIAS_7V0: MICBIAS is set to 7.0 V
 * @ADAU1977_MICBIAS_7V5: MICBIAS is set to 7.5 V
 * @ADAU1977_MICBIAS_8V0: MICBIAS is set to 8.0 V
 * @ADAU1977_MICBIAS_8V5: MICBIAS is set to 8.5 V
 * @ADAU1977_MICBIAS_9V0: MICBIAS is set to 9.0 V
 */
enum adau1977_micbias {
    ADAU1977_MICBIAS_5V0 = 0x0,
    ADAU1977_MICBIAS_5V5 = 0x1,
    ADAU1977_MICBIAS_6V0 = 0x2,
    ADAU1977_MICBIAS_6V5 = 0x3,
    ADAU1977_MICBIAS_7V0 = 0x4,
    ADAU1977_MICBIAS_7V5 = 0x5,
    ADAU1977_MICBIAS_8V0 = 0x6,
    ADAU1977_MICBIAS_8V5 = 0x7,
    ADAU1977_MICBIAS_9V0 = 0x8,
};
 
/**
 * struct adau1977_platform_data - Platform configuration data for the ADAU1977
 * @micbias: Specifies the voltage for the MICBIAS pin
 */
struct adau1977_platform_data {
    enum adau1977_micbias micbias;
};

Voltage Supplies

The ADAU1977 driver expects a regulator supply for the AVDD voltage to be specified. This allows the driver to disable the supply when not needed, which helps to conserve power. Additionally the driver optionally accepts a supply for the DVDD voltage. If a supply for the DVDD voltage is specified the driver will use that regulator and disable the internal LDO.

When using machine board files to register the device the regulators can be specified in the board file.

static struct regulator_consumer_supply adau1977_avdd_consumer_supplies[] = {
	REGULATOR_SUPPLY("AVDD", "spi0.1"),
};
 
static struct regulator_init_data adau1977_avdd_reg_init_data = {
	.constraints	= {
		.name	= "3V3",
		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
	},
	.consumer_supplies = adau1977_avdd_consumer_supplies,
	.num_consumer_supplies = ARRAY_SIZE(adau1977_avdd_consumer_supplies),
};
 
static struct fixed_voltage_config adau1977_avdd_pdata = {
	.supply_name	= "board-3V3",
	.microvolts	= 3300000,
	.gpio		= -EINVAL,
	.enabled_at_boot = 0,
	.init_data	= &adau1977_avdd_reg_init_data,
};
 
static struct platform_device adau1977_avdd_voltage_regulator = {
	.name		= "reg-fixed-voltage",
	.id		= 0,
	.dev		= {
		.platform_data	= &adau1977_avdd_pdata,
	},
};
 
/* Optional */
static struct regulator_consumer_supply adau1977_dvdd_consumer_supplies[] = {
	REGULATOR_SUPPLY("DVDD", "spi0.1"),
};
 
static struct regulator_init_data adau1977_dvdd_reg_init_data = {
	.constraints	= {
		.name	= "1V8",
		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
	},
	.consumer_supplies = adau1977_dvdd_consumer_supplies,
	.num_consumer_supplies = ARRAY_SIZE(adau1977_dvdd_consumer_supplies),
};
 
static struct fixed_voltage_config adau1977_dvdd_pdata = {
	.supply_name	= "board-1V8",
	.microvolts	= 1800000,
	.gpio		= -EINVAL,
	.enabled_at_boot = 0,
	.init_data	= &adau1977_dvdd_reg_init_data,
};
 
static struct platform_device adau1977_dvdd_voltage_regulator = {
	.name		= "reg-fixed-voltage",
	.id		= 1,
	.dev		= {
		.platform_data	= &adau1977_dvdd_pdata,
	},
};

When using devicetree the regulators should be specified in the devicetree. For more on how to specify the regulators using devicetree see the regulator devicetree bindings documentation and the devicetree examples below in the SPI and I2C sections.

Devicetree bindings

The ADAU1977 driver is fully devicetree compatible. Depending on the bus type the device is connected to different properties must/can be specified.

Shared required properties:

  • compatible: Must be “adi,adau1977”, “adi,adau1978” or adi,adau1979”
  • AVDD-supply: A handle to the power supply connected to the AVDD pin

Shared optional properties:

  • DVDD-supply: A handle to the power supply connected to the DVDD pin. If specified the internal LDO will be disabled.
  • reset-gpios: GPIO chip handle and specifier that define which GPIO is connected to the chips PD/RST pin. If not specified the reset pin is assumed to be hardwired to VCC.
  • adi,micbias: Configures the voltage setting for the MICBIAS pin. If not specified the default value will be 8.5 Volts. This property is only valid for the ADAU1977. Valid values for this property are:
    • 0: 5.0V
    • 1: 5.5V
    • 2: 6.0V
    • 3: 6.5V
    • 4: 7.0V
    • 5: 7.5V
    • 6: 8.0V
    • 7: 8.5V
    • 8: 9.0V

I2C required properties:

  • reg: The I2C address of the chip

SPI required properties:

  • reg: The SPI chipselect signal of the SPI master associated with this chip
  • spi-max-frequency: Maximum spi frequency to use.

I2C

Declaring I2C devices

Unlike PCI or USB devices, I2C devices are not enumerated at the hardware level. Instead, the software must know which devices are connected on each I2C bus segment, and what address these devices are using. For this reason, the kernel code must instantiate I2C devices explicitly. There are different ways to achieve this, depending on the context and requirements. However the most common method is to declare the I2C devices by bus number.

This method is appropriate when the I2C bus is a system bus, as in many embedded systems, wherein each I2C bus has a number which is known in advance. It is thus possible to pre-declare the I2C devices that inhabit this bus. This is done with an array of struct i2c_board_info, which is registered by calling i2c_register_board_info().

So, to enable such a driver one need only edit the board support file by adding an appropriate entry to i2c_board_info.

For more information see: Documentation/i2c/instantiating-devices

21 Oct 2010 16:10 · Michael Hennerich

The I2C device id depends on the ADDR0 and ADDR1 pin settings and needs to be set according to your board setup.

ADDR1 ADDR0 I2C device id
0 0 0x11
0 1 0x31
1 0 0x51
1 1 0x71

In this example we assume ADDR0=0 and ADDR1=0.

static struct adau1977_platform_data adau1977_pdata = {
	.micbias = ADAU1977_MICBIAS_6V5,
};
 
static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
 
	[--snip--]
	{
		I2C_BOARD_INFO("adau1977", 0x11),
		.platform_data = &adau1977_pdata,
	},
	[--snip--]
}
static int __init stamp_init(void)
{
	[--snip--]
	i2c_register_board_info(0, bfin_i2c_board_info,
				ARRAY_SIZE(bfin_i2c_board_info));
	[--snip--]
 
	return 0;
}
arch_initcall(board_init);

Devicetree

codec_supply: fixedregulator@0 {
	compatible = "regulator-fixed";
	regulator-name = "AVDD";
	regulator-min-microvolt = <3300000>;
	regulator-max-microvolt = <3300000>;
};

i2c@41600000 {
	compatible = "...;
	...

	#size-cells = <0>;
	#address-cells = <1>;
            
	adau1977: codec@11 {
		compatible = "adi,adau1977";
		reg = <0x11>;
		reset-gpios = <&gpio 5 0>;
		AVDD-supply = <&codec_supply>;
	};
};

SPI

Declaring SPI slave devices

Unlike PCI or USB devices, SPI devices are not enumerated at the hardware level. Instead, the software must know which devices are connected on each SPI bus segment, and what slave selects these devices are using. For this reason, the kernel code must instantiate SPI devices explicitly. The most common method is to declare the SPI devices by bus number.

This method is appropriate when the SPI bus is a system bus, as in many embedded systems, wherein each SPI bus has a number which is known in advance. It is thus possible to pre-declare the SPI devices that inhabit this bus. This is done with an array of struct spi_board_info, which is registered by calling spi_register_board_info().

For more information see: Documentation/spi/spi-summary

21 Oct 2010 16:10 · Michael Hennerich
static struct adau1977_platform_data adau1977_pdata = {
	.micbias = ADAU1977_MICBIAS_7V5,
};
 
static struct spi_board_info board_spi_board_info[] __initdata = {
	[--snip--]
	{
		.modalias = "adau1977",
		.max_speed_hz = 10000000,     /* max spi clock (SCK) speed in HZ */
		.bus_num = 0,
		.chip_select = GPIO_PF10 + MAX_CTRL_CS, /* CS, change it for your board */
		.mode = SPI_MODE_0,
		.platform_data = &adau1977_pdata,
	},
	[--snip--]
};
static int __init board_init(void)
{
	[--snip--]
 
	spi_register_board_info(board_spi_board_info, ARRAY_SIZE(board_spi_board_info));
 
	[--snip--]
 
	return 0;
}
arch_initcall(board_init);

Devicetree

codec_supply: fixedregulator@0 {
	compatible = "regulator-fixed";
	regulator-name = "AVDD";
	regulator-min-microvolt = <3300000>;
	regulator-max-microvolt = <3300000>;
};

spi@41600000 {
	compatible = "...;
	...

	#size-cells = <0>;
	#address-cells = <1>;
            
	adau1977: codec@1 {
		compatible = "adi,adau1977";
		reg = <0x1>;
		spi-max-frequency = <10000000>;
		reset-gpios = <&gpio 5 0>;
		AVDD-supply = <&codec_supply>;
		adi,micbias = <5>;
	};
};

ASoC DAPM Widgets

Name Description Configuration
AIN1 Analog Input Channel 1
AIN2 Analog Input Channel 2
AIN3 Analog Input Channel 3
AIN4 Analog Input Channel 4
VREF Reference Voltage
MICBIAS Microphone Bias Output ADAU1977 only

ALSA Controls

Name Description
ADC1 Capture Volume Post ADC gain for channel 1
ADC2 Capture Volume Post ADC gain for channel 2
ADC3 Capture Volume Post ADC gain for channel 3
ADC4 Capture Volume Post ADC gain for channel 4
ADC1 Highpass-Filter Capture Switch Enables/disables the highpass-filter for channel 1
ADC2 Highpass-Filter Capture Switch Enables/disables the highpass-filter for channel 2
ADC3 Highpass-Filter Capture Switch Enables/disables the highpass-filter for channel 3
ADC4 Highpass-Filter Capture Switch Enables/disables the highpass-filter for channel 4
ADC1 DC Substraction Capture Switch Enables/disables DC offset subtraction for channel 1
ADC2 DC Substraction Capture Switch Enables/disables DC offset subtraction for channel 2
ADC3 DC Substraction Capture Switch Enables/disables DC offset subtraction for channel 3
ADC4 DC Substraction Capture Switch Enables/disables DC offset subtraction for channel 4

DAI configuration

The codec driver registers one DAI: adau1977-hifi

Supported DAI formats

Name Supported by driver Description Notes
SND_SOC_DAIFMT_I2S yes I2S Justified mode
SND_SOC_DAIFMT_RIGHT_J yes Right Justified mode
SND_SOC_DAIFMT_LEFT_J yes Left Justified mode
SND_SOC_DAIFMT_DSP_A yes data MSB after FRM LRC
SND_SOC_DAIFMT_DSP_B yes data MSB during FRM LRC
SND_SOC_DAIFMT_AC97 no AC97 mode
SND_SOC_DAIFMT_PDM no Pulse density modulation
SND_SOC_DAIFMT_NB_NF yes Normal bit- and frameclock
SND_SOC_DAIFMT_NB_IF yes Normal bitclock, inverted frameclock
SND_SOC_DAIFMT_IB_NF yes Inverted frameclock, normal bitclock
SND_SOC_DAIFMT_IB_IF yes Inverted bit- and frameclock
SND_SOC_DAIFMT_CBM_CFM yes Codec bit- and frameclock master Master mode is not supported if the LRCLK is used as clock source
SND_SOC_DAIFMT_CBS_CFM no Codec bitclock slave, frameclock master
SND_SOC_DAIFMT_CBM_CFS no Codec bitclock master, frameclock slave
SND_SOC_DAIFMT_CBS_CFS yes Codec bit- and frameclock slave

System clock configuration

The sysclk can either be provided by the internal PLL derived from the MCLK signal or by the LRCLK signal.

enum adau1977_clk_id {
    ADAU1977_SYSCLK,
};
 
enum adau1977_sysclk_src {
    ADAU1977_SYSCLK_SRC_MCLK,
    ADAU1977_SYSCLK_SRC_LRCLK,
};

If the MCLK is used the supported sampling rates of the device is constraint to those sample rates that can be derived from the the MCLK. The constraints are setup in the CODEC driver's startup routine. This means that the sysclk needs to be configured before the CODEC's startup callback is called. This can either be done from the machine driver's startup or init callback.

For some hardware configurations it might be possible to change the MCLK frequency at runtime (e.g. to support both 44.1kHz and 48kHz based sample rates). In this case in order for the CODEC driver to not set any sample rate constraints the sysclk frequency should be set to 0 until it has been decided which MCLK frequency is used. This can for example be implemented by setting the sysclk to 0 in the machine driver's startup callback and setting it to the actual frequency in the hw_params callback.

TDM configuration

The TDM mode of the ADAU1977 can be configured using the snd_soc_dai_set_tdm_slot() function.

  • The number of slots can be either 2, 4, 8 or 16. If the number of slots is set to 0 the device will go to I2S mode.
  • The slot width can be 16, 24, 32 bits per slot (In master mode only 16 and 32 bits per slot are supported)
  • tx_mask must always be 0.
  • Each slot can hold the data sample of one channel. Which slot holds which channel is configured by rx_mask. The first bit that is set in rx_mask is the slot that holds the first channel, the second bit the slot that holds the second channel and so on. If less then 4 bits are set the channels for which no slot has been provided are disabled. (E.g. 0x33, configures channel 0 to slot 0, channel 1 to slot 1, channel 2 to slot 4 and channel 3 to slot 4).

Example TDM configuration:

	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0f, 4, 32);

To configure whether the unused TDM slots should be tristated or actively driven low the snd_soc_dai_set_tristate() function is used. By default they are driven low.

Example tristate configuration:

	ret = snd_soc_dai_set_tristate(codec_dai, true);

Example machine driver configuration

Fixed MCLK

static int eval_adau1977_init(struct snd_soc_pcm_runtime *rtd)
{
	return snd_snd_soc_codec_set_sysclk_set_sysclk(rtd->codec, ADAU1977_SYSCLK,
			ADAU1977_SYSCLK_SRC_MCLK, 1228000, SND_SOC_CLOCK_IN);
}
 
static struct snd_soc_dai_link eval_adau1977_dai = {
	.name = "adau1977",
	.stream_name = "ADAU1977", 
	.cpu_dai_name = "bfin-i2s.0", 
	.codec_dai_name = "adau1977-hifi",
	.platform_name = "bfin-i2s-pcm-audio",
	.codec_name = "adau1977.0-0011",
	.init = eval_adau1977_init,
	.dai_fmt = SND_SOC_DAIFMT_I2S |
		SND_SOC_DAIFMT_NB_NF |
		SND_SOC_DAIFMT_CBM_CFM,
};

Configurable MCLK

static int eval_adau1977_init(struct snd_soc_pcm_runtime *rtd)
{
	return snd_snd_soc_codec_set_sysclk_set_sysclk(rtd->codec, ADAU1977_SYSCLK,
			ADAU1977_SYSCLK_SRC_MCLK, 0, SND_SOC_CLOCK_IN);
}
 
static int eval_adau1977_hw_params(struct snd_pcm_substream *substream,
	struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	int ret;
 
	if (params_rate(params) % 441000 == 0)
		mclk_rate = 11289600;
	else
		mclk_rate = 12288000;
 
	clk_set_rate(mclk, mclk_rate);
 
	return snd_snd_soc_codec_set_sysclk_set_sysclk(rtd->codec, ADAU1977_SYSCLK,
			ADAU1977_SYSCLK_SRC_MCLK, mclk_rate, SND_SOC_CLOCK_IN);
}
 
static struct snd_soc_ops eval_adau1977_ops = {
	.hw_params = bfin_eval_adau1x61_hw_params,
};
 
static struct snd_soc_dai_link eval_adau1977_dai = {
	.name = "adau1977",
	.stream_name = "ADAU1977", 
	.cpu_dai_name = "bfin-i2s.0", 
	.codec_dai_name = "adau1977-hifi",
	.platform_name = "bfin-i2s-pcm-audio",
	.codec_name = "adau1977.0-0011",
	.ops = &eval_adau1977_ops,
	.init = eval_adau1977_init,
	.dai_fmt = SND_SOC_DAIFMT_I2S |
		SND_SOC_DAIFMT_NB_NF |
		SND_SOC_DAIFMT_CBM_CFM,
};