Sound card driver 06 - Implementation of alsa driver - virtual sound card - widget

Platform: ubuntu 16.04, the kernel version is 4.15.0. Theoretically, any platform can, even android, as long as it can be compiled.

Functions to be completed: there may be multiple recording channels in a codec. What should I do if I want to open a channel for recording?

Purpose: just like doing a math problem, read the answer once and think you understand it. If not, you may not be able to do it when you really do it yourself. Then implement it in your own driver.

This paper only pursues application, not principle. For details, see the official documents or https://blog.csdn.net/droidphone/category_1118446.html.

What are widget s for

Widgets can also control registers. The question is, with kcontrol, why should there be widget s?

widget is the plug-in of kcontrol, which can be understood as the further upgrading and packaging of kcontrol. It also refers to a component in the audio system, such as mixer, mux, input / output pin, power supply, etc.

kcontrol has the following shortcomings:

  • It can only describe itself, but cannot describe the connection relationship between various kcontrol s;
  • There is no corresponding power management mechanism;
  • There is no corresponding time processing mechanism to respond to audio events such as play, stop, power on and power off;
  • In order to prevent pop pop sound, the user program needs to pay attention to the power on and power off sequence of each kcontrol;
  • When an audio path is no longer valid, all kcontrol s on the path cannot be automatically closed;

So the widget was born.

codec like a map

The following is the module diagram of internal codec of Quanzhi A33

Like a map.

When the recording is input from mic1, it passes through each mixer/mux, just like passing through each level, and finally reaches ADCL, and the analog signal is converted into digital signal.

All roads lead to Rome, and there are thousands of roads to Rome, but we only need to take one of them, not all of them; Similarly, the signal that can reach the ADCL can be mic1, mic2 or others, but when I only need mic1 input, other channels do not need to be enabled. The path that needs to be enabled is called complete path.

The purpose is to save electricity.

Construct two complete path s

In order to better understand dapm, two complete path s are constructed in their own virtual sound card driver.

First look at the road map, as follows:

Route 1: mic1 -- > kcontrol (a switch) - > mixer -- > adcl is a recording

Route 2: DACL -- > hpout -- > speaker (power amplifier), is a play

Let's look at the code implementation

Step 1: define widget s

static const struct snd_kcontrol_new mic1_input_mixer[] = {
	SOC_DAPM_SINGLE("MIC1 Boost Switch", VCODEC_ADCL_REG, 0, 1, 0),
};

static const struct snd_soc_dapm_widget vcodec_dapm_widgets[] = {
	SND_SOC_DAPM_INPUT("MIC1"),
	SND_SOC_DAPM_MIXER("ADCL Input", SND_SOC_NOPM, 0, 0,
				mic1_input_mixer,
				ARRAY_SIZE(mic1_input_mixer)),
	SND_SOC_DAPM_AIF_OUT_E("ADCL", "Capture", 0, VCODEC_ADCL_REG,
				8, 0,
				vcodec_capture_event,
				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
	
	
	SND_SOC_DAPM_AIF_IN_E("DACL", "Playback", 0, VCODEC_DAC_REG,
				0, 0,
				vcodec_playback_event,
				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
	SND_SOC_DAPM_OUTPUT("HPOUTL"),
	SND_SOC_DAPM_SPK("HpSpeaker", vcodec_hpspeaker_event),
};

This is an array whose members are snd_soc_dapm_widget, which fills different types of widgets through various macro definitions;

  • SND_SOC_DAPM_INPUT: the widget corresponds to an input pin; A complete dapm audio path must have a starting point and an ending point. The widgets located at these starting points and ending points are called endpoint widgets. Obviously, mic1 is the starting point and ADCL is the end point;

  • SND_SOC_DAPM_MIXER: this widget corresponds to a mixer control. Take a look at the macro definition:

    #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
    	 wcontrols, wncontrols)\
    {	.id = snd_soc_dapm_mixer, .name = wname, \
    	SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    	.kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
    

    Parameter Description:

    • id: indicates the type of the widget, snd_ soc_ dapm_ A mixer is a mixer control;
    • wname: name of the widget;
    • wreg: the register control bit corresponding to the widget, which is used to control the power state of the widget. If not, it is set to SND_SOC_NOPM;
    • winvert: whether the set value is logically reversed;
    • Wccontrols: kcontrol indicates a switch. A mixer may have multiple inputs. A kcontrol indicates a switch, which is m in the figure above;
    • wncontrols: number of kcontrol s;
  • SND_SOC_DAPM_AIF_OUT_E: Corresponding to a digital audio output interface, see the macro definition:

    #define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \
    			     wevent, wflags)				\
    {	.id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
    	SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    	.event = wevent, .event_flags = wflags }
    

    stname must match codec dai_driver's stream_name is the same;

    You must be careful to find that this macro has a "_E" suffix behind it. Compared with the macro without "_E", there are more wevent and wflags parameters. Wevent is used to save the event callback function of the widget, and wflags is used to save the types of dapm events that the widget needs to care about, Only the event with the corresponding event bit set in wflags will be sent to the event callback function for processing. What are the types of events in dapm?

    Event typeexplain
    SND_SOC_DAPM_PRE_PMUEvent to be emitted before the widget is powered on
    SND_SOC_DAPM_POST_PMUEvent to be emitted after the widget is powered on
    SND_SOC_DAPM_PRE_PMDEvent to be emitted before the widget is powered off
    SND_SOC_DAPM_POST_PMDEvent to be emitted after the widget is powered off
    SND_SOC_DAPM_PRE_REGEvents issued before audio path setting
    SND_SOC_DAPM_POST_REGEvents emitted after audio path setting
    SND_SOC_DAPM_WILL_PMUProcessing up_ Events issued before list
    SND_SOC_DAPM_WILL_PMDProcessing down_ Events issued before list
    SND_SOC_DAPM_PRE_POST_PMDSND_SOC_DAPM_PRE_PMD and
    SND_ SOC_ DAPM_ POST_ Consolidation of PMD
  • SND_SOC_DAPM_OUTPUT: the widget corresponds to an output pin;

  • SND_SOC_DAPM_SPK: the widget corresponds to a speaker;

Step 2: register widgets

snd_soc_dapm_new_controls(dapm, vcodec_dapm_widgets,
				ARRAY_SIZE(vcodec_dapm_widgets));

Widgets will eventually be added to the card's widgets list

Step 3: set route

static const struct snd_soc_dapm_route vcodec_dapm_routes[] = {
	/* Mic input route */
	{"ADCL Input", "MIC1 Boost Switch", "MIC1"},
	{"ADCL", NULL, "ADCL Input"},
	
	/* Headphone output route */
	{"HPOUTL", NULL, "DACL"},
	{"HpSpeaker", NULL, "HPOUTL"},
	
};

A widget has input and output, and widgets can be dynamically connected. The "line" connecting two widgets is path, which connects the output of one widget with the input of another widget;

What's route for? It can be understood that route is used to generate a path. Route specifies the widget at the output and the widget at the input, and then generates a path connecting the two widgets;

For example, in the above array, "ADCL Input" is the destination widget, "MIC1 Boost Switch" is the switch (kcontrol) between two widgets, no is NULL, "MIC1" is the starting widget;

Step 4: register routes

snd_soc_dapm_add_routes(dapm, vcodec_dapm_routes,
				ARRAY_SIZE(vcodec_dapm_routes));

snd_ soc_ dapm_ add_ What the routes () function does is to find the destination widget, start widget and switch (kcontrol) specified by the route, generate the path, and then add the path to the paths linked list of the card.

How to use it?

One of the preconditions for dapm to power on a widget is that the widget is located on a complete audio path, and the two ends of a complete audio path must be input / output pins, or an external audio device, or an audio stream widget in the active state;

Take the widget and route just defined as an example, "MIC1" is the input endpoint widget, "ADCL" is the output endpoint widget, then

"Mic1" - > "adcl input" - > "adcl" is a complete path. If the switch of this complete path("MIC1 Boost Switch") is enabled, all widget s on this complete path will be powered on when recording is turned on;

Similarly, "DACL" - > "hpout" - > "hpspeaker" is a complete path. Because this complete path has no switch, all widgets on this complete path will be powered on when playing is turned on. Of course, the event function set by the widget will also be called at this time. On the contrary, at the end of playback, all widgets on the complete path will be powered down, and the event function set by the widget will also be called;

Take a look at this article: The sixth detailed explanation of DAPM in ALSA sound card drive: the essence is to move the whole body

How do you know when to power up / down a complete path? Of course, you have to scan it,

The following situations can trigger dapm to initiate a scan operation:

  1. Sound card initialization stage;
  2. The configuration value of kcontrol is modified in user space;
  3. Opening or closing of pcm;
  4. The driver changes the widget and adds it to the dapm_dirty linked list.

test

Additional drivers need to be installed for testing under ubuntu, as follows:

sudo insmod /lib/modules/4.15.0-112-generic/kernel/sound/core/snd-compress.ko
sudo insmod /lib/modules/4.15.0-112-generic/kernel/sound/core/snd-pcm-dmaengine.ko
sudo insmod /lib/modules/4.15.0-112-generic/kernel/sound/soc/snd-soc-core.ko

Why / lib/modules/4.15.0-112-generic/kernel/sound/core / please refer to the previous articles.

Then install our driver

sudo insmod vplatform.ko
sudo insmod vcodec.ko
sudo insmod vmachine.ko

If you are testing on the development board, you don't need to be so troublesome. Install the driver directly

After installing the driver, you can see your sound card as follows:

vbox@vbox-pc:~$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: I82801AAICH [Intel 82801AA-ICH], device 0: Intel ICH [Intel 82801AA-ICH]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: mycodec [my-codec], device 0: MY-CODEC vcodec_dai-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0

It can be seen that we registered card 1. ubuntu itself has its own sound card. Let's take a look at the registered controls

vbox@vbox-pc:~$ amixer -D hw:1 contents
numid=2,iface=MIXER,name='ADCL Input MIC1 Boost Switch'
  ; type=BOOLEAN,access=rw------,values=1
  : values=on
numid=1,iface=MIXER,name='DAC volume'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=255,step=0
  : values=50,60
  | dBscale-min=-119.25dB,step=0.75dB,mute=0

Well, I didn't see the registration widget. Did the registration fail? did not

You can take a look at the / sys / kernel / debug / ASoC / my CODEC / codec: vcodec.0/dapm directory

vbox@vbox-pc:~$ sudo ls -l /sys/kernel/debug/asoc/my-codec/codec\:vcodec\.0/dapm
total 0
-r--r--r-- 1 root root 0 11 June 15-18:58 ADCL
-r--r--r-- 1 root root 0 11 June 15-18:58 ADCL Input
-r--r--r-- 1 root root 0 11 June 15-18:58 bias_level
-r--r--r-- 1 root root 0 11 June 15-18:58 Capture
-r--r--r-- 1 root root 0 11 June 15-18:58 DACL
-r--r--r-- 1 root root 0 11 June 15-18:58 HPOUTL
-r--r--r-- 1 root root 0 11 June 15-18:58 HpSpeaker
-r--r--r-- 1 root root 0 11 June 15-18:58 MIC1
-r--r--r-- 1 root root 0 11 June 15-18:58 Playback

ps: you can only see it with root permission under ubuntu

You can open 'ADCL Input MIC1 Boost Switch'

vbox@vbox-pc:~$ amixer -D hw:1 cset numid=2 on
numid=2,iface=MIXER,name='ADCL Input MIC1 Boost Switch'
  ; type=BOOLEAN,access=rw------,values=1
  : values=on

You will find that the name of this kcontrol has changed. It was defined as "MIC1 Boost Switch" and now it has become "ADCL Input MIC1 Boost Switch";

If recording is in progress at this time, all widgets on the corresponding complete path will be powered on, and the event function of the widget will also be called;

Check the following log to see if it is clearer

[ 2294.464437] vplat_pcm_open,line:235
[ 2294.464441] -vcodec_startup,line:193
[ 2294.464630] my_card_hw_params,rate:48000
[ 2294.464632] -vcodec_hw_params,line:205
[ 2294.464633] playback period size : 32768
[ 2294.464667] -vcodec_prepare,line:247
[ 2294.464697] -vcodec_playback_event,SND_SOC_DAPM_PRE_PMU
[ 2294.464700] -vcodec_reg_read,line:163,reg_data[2] = 0x0
[ 2294.464701] -vcodec_reg_write,line:171,reg=0x2,val=0x1
[ 2294.464703] -vcodec_hpspeaker_event,SND_SOC_DAPM_POST_PMU
[ 2294.466124] vplat_pcm_open,line:235
[ 2294.466127] -vcodec_startup,line:193
[ 2294.466251] my_card_hw_params,rate:48000
[ 2294.466253] -vcodec_hw_params,line:205
[ 2294.466254] capture period size : 32768
[ 2294.466289] -vcodec_prepare,line:247
[ 2294.466314] -vcodec_reg_read,line:163,reg_data[1] = 0x1
[ 2294.466315] -vcodec_reg_write,line:171,reg=0x1,val=0x101
[ 2294.466317] -vcodec_capture_event,SND_SOC_DAPM_POST_PMU
[ 2294.466336] -vcodec_prepare,line:247
[ 2294.466355] -vcodec_prepare,line:247
[ 2294.473240] -vcodec_trigger: catpure start
[ 2294.473243] capture running...
[ 2294.492381] -vcodec_trigger: playback start
[ 2294.492383] playback running...
[ 2304.140557] -vcodec_trigger:playback stop
[ 2304.140559] playback stop...
[ 2304.358123] -vcodec_shutdown,line:212
[ 2304.358126] vplat_pcm_close,line:248
[ 2304.358150] -vcodec_hpspeaker_event,SND_SOC_DAPM_PRE_PMD
[ 2304.358153] -vcodec_reg_read,line:163,reg_data[2] = 0x1
[ 2304.358155] -vcodec_reg_write,line:171,reg=0x2,val=0x0
[ 2304.358156] -vcodec_playback_event,SND_SOC_DAPM_POST_PMD
[ 2304.358178] -vcodec_trigger:catpure stop
[ 2304.358179] capture stop...
[ 2304.358235] -vcodec_shutdown,line:212
[ 2304.358236] vplat_pcm_close,line:248
[ 2304.358250] -vcodec_reg_read,line:163,reg_data[1] = 0x101
[ 2304.358252] -vcodec_reg_write,line:171,reg=0x1,val=0x1
[ 2304.358253] -vcodec_capture_event,SND_SOC_DAPM_POST_PMD

code

Code location: https://codechina.csdn.net/u014056414/myalsa

Other functions will be added later. The submission of this article is: 9. Add widget s to simulate two complete path s of codec

Beginners can follow this submission to learn, so as to avoid the interference of new submission.

Keywords: Linux Embedded system Alsa

Added by h3ktlk on Fri, 19 Nov 2021 07:40:20 +0200