Linux ALSA Audio Driver Seven: Codec__linux in ASOC architecture

Source: Internet
Author: User
Tags data structures volatile

1. Codec Introduction

In mobile devices, the role of codec can be summed up to 4 kinds, respectively:

For PCM and other signals D/a conversion, the digital audio signal to analog signals to the mic, LineIn or other input source analog signals for A/D conversion, the analog sound signal conversion CPU can handle digital signals to control the audio channel, such as playing music, listening to FM radio, or when answering the phone, the flow of the audio signal in the codec is different to the audio signal processing, such as volume control, power amplification, EQ control, etc.

ASOC has defined some of the corresponding interfaces for codec to easily control the codec. One of the basic requirements of ASOC for codec drivers is that the driver code must be platform independent so that the same codec code can be used on different platforms without modification. The following discussion is based on the Wolfson codec chip Wm8994,kernel version 3.3.x.


/*****************************************************************************************************/
Statement: This Bo content by Http://blog.csdn.net/droidphone Original, reproduced please indicate the source, thank you.
/*****************************************************************************************************/

The most important data structures for codec in

2.  asoc Describe codec are: SND_SOC_CODEC,SND_SOC_CODEC_DRIVER,SND_SOC_ Dai,snd_soc_dai_driver, where the Snd_soc_dai and Snd_soc_dai_driver are also used in ASOC platform drive, platform and codec's Dai through Snd_soc_dai _link structure that binds the connection in the machine drive. Let's take a look at the definition of these structures, here I only post the fields I want to focus on, detailed definition please refer to:/include/sound/soc.h. Snd_soc_codec:

/* SoC Audio CODEC device * *
struct SND_SOC_CODEC {
	const char *name;  /* Codec's name
	/* struct device *dev;/* Pointer to codec device/
	const struct Snd_soc_codec_driver *driver;/* The driver pointer to the codec * *
	struct snd_soc_card *card;    /* point to Machine drive card instance/
	int Num_dai/* The number of the codec digital interface, more and more codec with multiple I2S or PCM interface
	/INT (*volatile_register )(...);  /* is used to determine whether a register is a volatile/
	int (*readable_register) (...);  /* is used to determine whether a register can be read/
	int (*writable_register) (...);  /* Used to determine whether a register can be written/

	* Runtime
	/* ... /* codec IO
	/void *control_data/* The structure to which the pointer refers is used for control of the codec, and is usually combined with the Read,write field/
	enum Snd_soc_control_type control_type;/* can be A/
	unsigned int (*read) in Snd_soc_spi,snd_soc_i2c,snd_soc_regmap (struct Snd_soc_codec *, unsigned int);  /* Read codec register function/
	int (*write) (struct Snd_soc_codec *, unsigned int, unsigned int);  /* Write codec Register function *
	////* DAPM * * struct snd_soc_dapm_context dapm;  /* for DAPM control *
/};

Snd_soc_codec_driver:
/* Codec driver * * struct Snd_soc_codec_driver {/* driver ops/INT (*probe) (struct snd_soc_codec *);  
	/* Codec driven probe function, by Snd_soc_instantiate_card callback/INT (*remove) (struct snd_soc_codec *);  Int (*suspend) (struct snd_soc_codec *);  /* Power Management/INT (*resume) (struct snd_soc_codec *);  /* Power Management/* Default control and Setup, added after probe () is run/const struct SND_KCONTROL_NEW *controls;  /* Audio control pointer/const struct SND_SOC_DAPM_WIDGET *dapm_widgets;  /* DAPM parts pointer/const struct SND_SOC_DAPM_ROUTE *dapm_routes;  /* DAPM routing pointer////* Codec wide Operations/* INT (*SET_SYSCLK) (...);  /* Clock configuration function */int (*SET_PLL) (...);  /* Phase-locked loop configuration Function///////codec IO/unsigned int (*read) (...);  /* Read CODEC register function */int (*write) (...);  /* Write codec Register function */int (*volatile_register) (...);  /* is used to determine whether a register is a volatile/int (*readable_register) (...);  /* is used to determine whether a register can be read/int (*writable_register) (...);  /* Used to determine whether a register can be written/* codec bias level/INT (*set_bias_level) (...); /* Bias voltage configuration function */}; 
Snd_soc_dai:
 * * Digital Audio Interface runtime data.
 *
 * Holds runtime data for a DAI.
 * *
struct Snd_soc_dai {
	const char *name;  /* DAI's name * *
	struct device *dev;  /* Device Pointers

	/* Driver ops
	/* struct snd_soc_dai_driver *driver;  /* The pointer to the DAI drive structure

	////* Dai Runtime Info *
	/unsigned int capture_active:1;		/* stream is in use
	/unsigned int playback_active:1;		/* stream is in use

	//* DAI DMA data
	/void *playback_dma_data;  /* for managing Playback DMA
	/void *capture_dma_data;  /* Used to manage capture DMA

	/* Parent PLATFORM/CODEC
	/union {
		struct snd_soc_platform *platform;  /* If it is CPU Dai, point to the bound platform * *
		struct SND_SOC_CODEC *codec;  /* If the codec Dai points to the bound codec * *
	;
	struct Snd_soc_card *card;  /* point to Machine driver Crad instance
/};
Snd_soc_dai_driver:
 * * * Digital Audio Interface Driver.
 *
 describes the Digital Audio Interface in terms to its ALSA, DAI and AC97
 * operations and capabilities. Codec and platform drivers'll register this
 * structure for every DAI they have.
 * This
 structure covers the clocking, formating and ALSA to each
 * operations.
 * *
struct Snd_soc_dai_driver {
	/* Dai description
	/const char  *name; /* DAI driver name/////

	* Dai driver callbacks/
	int (*probe) (struct Snd_soc_dai *dai);  /* Dai-driven probe function, by Snd_soc_instantiate_card callback/
	int (*remove) (struct Snd_soc_dai *dai);  
	Int (*suspend) (struct Snd_soc_dai *dai);  /* Power management/
	int (*resume) (struct Snd_soc_dai *dai);  

	/* OPS *
	/const struct SND_SOC_DAI_OPS *ops;  * * point to the SND_SOC_DAI_OPS structure of dai * * *

	DAI capabilities * *
	struct snd_soc_pcm_stream capture;  /* Describe capture ability * *
	struct snd_soc_pcm_stream playback;  * * Describe the ability of playback
/};
Snd_soc_dai_ops is used to implement the control box parameter configuration for this Dai:
struct Snd_soc_dai_ops {
	 * * * dai clocking configuration, all optional.
	 * Called by Soc_card drivers, normally in their hw_params.
	 */
	Int (*SET_SYSCLK) (...);
	Int (*SET_PLL) (...);
	Int (*set_clkdiv) (...);
	 * * * DAI format configuration
	 * Called by Soc_card drivers, normally in their hw_params.
	 */
	Int (*set_fmt) (...);
	Int (*set_tdm_slot) (...);
	Int (*set_channel_map) (...);
	Int (*set_tristate) (...);
	 * * * DAI digital mute-optional.
	 * Called by Soc-core to minimise any pops.
	 */
	Int (*digital_mute) (...);
	 * * ALSA PCM audio Operations-all Optional.
	 * Called by Soc-core during audio PCM operations.
	 */
	Int (*startup) (...);
	void (*shutdown) (...);
	Int (*hw_params) (...);
	Int (*hw_free) (...);
	Int (*prepare) (...);
	Int (*trigger) (...);
	 * * For hardware based FIFO caused delay reporting.
	 * Optional.
	 * *
	snd_pcm_sframes_t (*delay) (...);
3. Registration of CodecBecause codec-driven code is platform-independent, the first task for machine drivers to use the Codec,codec driver is to identify Snd_soc_codec and Snd_soc_dai instances and register them in the system. After registration, codec and Dai can be used for machine drive. Take WM8994 as an example, the corresponding code location:/SOUND/SOC/CODECS/WM8994.C, the module's entry function registers a platform driver:
static struct Platform_driver Wm8994_codec_driver = {
	. Driver = {
		   . Name = "Wm8994-codec",
		   . Owner = This_modul E,
		   },
	. Probe = Wm8994_probe,
	. Remove = __devexit_p (Wm8994_remove),
};

Module_platform_driver (Wm8994_codec_driver);
There are platform driver, there must be a corresponding platform device, this platform device source later, obviously, platform driver registration, probe callback will be called, this is wm8994_ Probe function:
static int __devinit wm8994_probe (struct platform_device *pdev)
{return
	Snd_soc_register_codec (&pdev- >dev, &soc_codec_dev_wm8994,
			Wm8994_dai, Array_size (Wm8994_dai));

Among them, soc_codec_dev_wm8994 and Wm8994_dai are defined as follows (3 Dai is defined in the code, only the first one is listed here):
static struct Snd_soc_codec_driver soc_codec_dev_wm8994 = {
	. Probe =	Wm8994_codec_probe,
	. remove =	Wm8994_codec_remove,
	. Suspend =	wm8994_suspend,
	. Resume =	Wm8994_resume,
	. Set_bias_level = Wm8994_set_bias_level,
	. reg_cache_size	= Wm8994_max_register,
	. Volatile_register = wm8994_soc_ volatile,
};
static struct Snd_soc_dai_driver wm8994_dai[] = {
	{
		. Name = ' Wm8994-aif1 ',
		. id = 1,
		. Playback = {
  .stream_name = "AIF1 Playback",
			. channels_min = 1,
			. Channels_max = 2,.
			rates = wm8994_rates,
			. formats = Wm8994_formats,
		},
		. Capture = {
			. Stream_name = "AIF1 capture",
			. channels_min = 1
			. Channels_max = 2,
			. Rates = Wm8994_rates,
			. formats = Wm8994_formats,
		 },
		. Ops = &wm8994_aif1_dai _ops,
	},
	...
}
Visible, the first step of the codec driver is to define an instance of Snd_soc_codec_driver and Snd_soc_dai_driver, and then invoke the SND_SOC_REGISTER_CODEC function to register the codec. Enter the SND_SOC_REGISTER_CODEC function to see: First, it applies an instance of a SND_SOC_CODEC structure:
	codec = kzalloc (sizeof (struct snd_soc_codec), gfp_kernel);
To determine the name of the codec, the name is very important, machine driver defined snd_soc_dai_link will specify each link's codec and Dai's name, the matching binding timing is by the name of the comparison, to find the codec.
/* Create CODEC Component Name *
	/codec->name = Fmt_single_name (dev, &codec->id);
It then initializes its fields, and the value of the multivalued segment comes from the instance of the Snd_soc_codec_driver defined above soc_codec_dev_wm8994:
	Codec->write = codec_drv->write;
	Codec->read = codec_drv->read;
	Codec->volatile_register = codec_drv->volatile_register;
	Codec->readable_register = codec_drv->readable_register;
	Codec->writable_register = codec_drv->writable_register;
	Codec->dapm.bias_level = Snd_soc_bias_off;
	Codec->dapm.dev = Dev;
	Codec->dapm.codec = codec;
	Codec->dapm.seq_notifier = codec_drv->seq_notifier;
	Codec->dapm.stream_event = codec_drv->stream_event;
	Codec->dev = Dev;
	Codec->driver = Codec_drv;
	Codec->num_dai = Num_dai;
After making some register cache initialization and configuration work, the Snd_soc_register_dais function is used to register the Dai in this codec:
	/* Register any DAIs *
	/if (Num_dai) {
		ret = Snd_soc_register_dais (Dev, dai_drv, num_dai);
		if (Ret < 0)
			goto fail;
	}
Finally, it links the codec instance to the global linked list codec_list, and the call to Snd_soc_instantiate_cards is a function triggering machine driver for a matching binding operation:
	List_add (&codec->list, &codec_list);
	Snd_soc_instantiate_cards ();
The Snd_soc_register_dais function is similar to SND_SOC_REGISTER_CODEC, showing that each Snd_soc_dai instance allocates memory, determines the name of Dai, and uses Snd_soc_dai_ Driver instance of the field to it to initialize the necessary, and finally the Dai link to the global linked list dai_list, and codec, and finally call the Snd_soc_instantiate_cards function to trigger a matching binding operation.
Figure 3.1 Dai's registration about the Snd_soc_instantiate_cards function, see another blog post: Linux audio driver Six: machine in ASOC architecture. 4. MFD Equipment

As mentioned earlier, the codec driver registers itself as a platform driver, that corresponds to the platform device where defined. The answer is in the following code file:/drivers/mfd/wm8994-core.c.

WM8994 itself has a variety of functions, in addition to codec, it is also used as Ldo and GPIO, these functions share some IO and interrupt resources, Linux provides a set of standard implementation methods for this device: MFD equipment. The basic idea is to implement a parent device for the public part of these functions, in order to share certain system resources and functions, then each child function is implemented as its sub device, so that both resources and code are shared, and a reasonable device hierarchy can be achieved, the main use of the API is: Mfd_add_devices (), Mfd_remove_devices (), mfd_cell_enable (), mfd_cell_disable (), Mfd_clone_cell ().

Back in wm8994-core.c, because WM8994 uses I2C for internal register access, it first registers a I2C driver:

static struct I2c_driver Wm8994_i2c_driver = {
	. Driver = {
		. Name = ' wm8994 ',
		. Owner = this_module,
		. PM = & Amp;wm8994_pm_ops,
		. of_match_table = Wm8994_of_match,
	},
	. Probe = Wm8994_i2c_probe,
	. remove = wm8994 _i2c_remove,
	. id_table = wm8994_i2c_id,
};

static int __init wm8994_i2c_init (void)
{
	int ret;

	ret = I2c_add_driver (&wm8994_i2c_driver);
	if (ret!= 0)
		pr_err ("Failed to register wm8994 I2C driver:%d\n", ret);

	return ret;
}
Module_init (Wm8994_i2c_init);
Entering the Wm8994_i2c_probe () function, it first applied for a wm8994 structure variable that was used as the driver_data of the I2C device, and it has been said that codec as its child device will take out and use this driver_data. Next, this function initializes and obtains a REGMAP structure using REGMAP_INIT_I2C (), which is used primarily for subsequent register I/O based on the regmap mechanism, and for regmap we stay behind to speak. Finally, add the MfD child device by Wm8994_device_init ():

static int wm8994_i2c_probe (struct i2c_client *i2c,
			    const struct I2C_DEVICE_ID *id)
{struct wm8994 *wm8994
	;
	int ret;
	wm8994 = Devm_kzalloc (&i2c->dev, sizeof (struct wm8994), gfp_kernel);
	I2c_set_clientdata (I2C, wm8994);
	Wm8994->dev = &i2c->dev;
	WM8994->IRQ = i2c->irq;
	Wm8994->type = id->driver_data;
	Wm8994->regmap = REGMAP_INIT_I2C (I2C, &wm8994_base_regmap_config);

	Return Wm8994_device_init (wm8994, I2C->IRQ);
}
Continue to the Wm8994_device_init () function, which first adds the MfD to the two ldo:
	/* ADD The on-chip regulators for bootstrapping
	/ret = mfd_add_devices (Wm8994->dev,-1,
			      Wm8994_regul Ator_devs,
			      array_size (Wm8994_regulator_devs),
			      NULL, 0);
Because the wm1811,wm8994,wm8958 three chips function similar, so the three chips all use WM8994 code, so Wm8994_device_init () next to different chip models do some initialization action, this part of the code is not posted. Next, get some configuration information from the Platform_data:
	if (pdata) {
		wm8994->irq_base = pdata->irq_base;
		Wm8994->gpio_base = pdata->gpio_base;

		/* GPIO configuration is only applied if it ' s Non-zero/...
	}
Finally, initialize the IRQ, and then add the codec and Gpio child devices:
	Wm8994_irq_init (wm8994);

	ret = Mfd_add_devices (Wm8994->dev,-1,
			      Wm8994_devs, Array_size (Wm8994_devs),
			      NULL, 0);
After these treatments, the I2C device, which is the parent device, is ready to hang 4 sub devices under it: Ldo-0,ldo-1,codec,gpio. Where the codec is added, it will match the platform driver of the codec described above, triggering the probe callback to complete the codec-driven initialization work described below.

5. Codec initialization Machine driver initialization, codec and Dai registration, will invoke Snd_soc_instantiate_cards () for a sound card and Codec,dai, Platform the matching binding process, here the binding, as described in the machine driver, is through the 3 global linked list, by name to match, the matching Codec,dai and platform instance assigned to the sound card each pair of Dai Snd_soc_pcm_ In the runtime variable. Once the binding succeeds, the codec and Dai-driven probe callbacks are invoked, and codec initialization is done in that callback. For WM8994, the callback is the Wm8994_codec_probe function:

Figure 5.1 Wm8994_codec_probe

Remove the driver_data of the parent device, which is actually the wm8994 structure variable in the previous section, take out the Regmap field and copy it to the codec control_data field; apply for a wm8994_priv private data structure, and set it to the driver_data of the codec device; After you complete this step, you can use Api:snd_soc_read (), Snd_soc_write ( The codec registers are read and written, the Driver_data (struct wm8994) and platform_data of the parent device are saved to the private structure Wm8994_priv, because 3 chip models are supported. This is based on the model of the chip to do some specific initialization work, the application of several necessary interrupts, set the appropriate bias level, through the snd_soc_update_bits to modify some registers, according to the parent device Platform_data, complete platform-specific initialization configuration;
Adding necessary CONTROL,DAPM parts to DAPM routing information;

This completes the initialization of the codec driver. 5.  regmap-io We know that in order to control codec, it is usually done by reading and writing its internal registers, and that the read-write interface is usually I2C or SPI interface, However, the bit composition of each codec chip register is different, and the bit bit of the register address is different. For example WM8753 's register address is 7bits, the data is 9bits,wm8993 The register address is 8bits, the data is also 16bits, but WM8994 's register address is 16bits, the data is also 16bits. In the kernel3.1 version, the kernel introduces a set of REGMAP mechanisms and associated APIs so that control of these diverse registers can be implemented in a unified operation. Regmap is also relatively simple to use: Define a REGMAP_CONFIG structure instance for codec, specify the address and data bits of the codec register, and, depending on the type of control bus of the codec, call one of the following functions to get a pointer to the REGMAP structure: struct Regmap *regmap_init_i2c (struct i2c_client *i2c, const-struct regmap_config);
struct Regmap *regmap_init_spi (struct spi_device *dev, const struct); The
assigns the obtained REGMAP structure pointer to the api:snd_soc_codec_set_cache_io of the codec->control_data; call Soc-io to associate Soc-io with Regmap; , the codec driver can read and write codec registers using APIs such as Snd_soc_read, Snd_soc_write, and Snd_soc_update_bits.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.