Linux Driver Development: wm8960 codec Code Analysis __linux

Source: Internet
Author: User

About the ALSA architecture has been gnawing for a long time, but also card for a long time. It's hard to say how much the fur really knows, no matter what, let's chew wm8960 codec's driver code first:

Necessary Correlation function Description:
////////////////////////////////////////////////////////////////////////////

1. #define Soc_enum_single (Xreg, Xshift, Xmax, xtexts) (Soc_enum_double, Xreg, Xshift, Xshift, Xmax
    )

# Define Soc_enum_double (Xreg, xshift_l, Xshift_r, xmax, xtexts) \
{   . reg = Xreg,. shift_l = xshift_l,. Shift_r = xs Hift_r, \
    . max = Xmax,. texts = xtexts, \
    . mask = xmax? Roundup_pow_of_two (Xmax)-1:0}
ex:
#define WM8 960_ALC3     0x13
static const char *wm8960_alcmode[] = {"ALC", "limiter"};
Soc_enum_single (WM8960_ALC3, 8, 2, Wm8960_alcmode)

The role of this macro definition: The BIT8 function of Reg 0x13 is select ALC mode,
Set 0:alc mode 1:limite mode.
1. Presumably guessing:
Soc_enum_single (Xreg, Xshift, Xmax, xtexts)
Defines a single enumerated control as follows:-

Xreg = Register
Xshift = control bit (s) offset in register
Xmask = control bit (s) size
Xtexts = Pointer to array of strings which describe each setting

Functions of the function:
Use this define to populate a particular structure to achieve the appropriate initialization settings:

/* Enumerated Kcontrol * *
struct Soc_enum {
    unsigned short reg;
    unsigned short reg2;
    unsigned char shift_l;
    unsigned char shift_r;
    unsigned int max;
    unsigned int mask;
    const char * const *TEXTS;
    const unsigned int *values;

Based on understanding, we find that the Soc_enum_single () is used to populate the SOC_ENUM structure if it is expanded:

Soc_enum_single (WM8960_ALC3, 8, 2, Wm8960_alcmode)
--->
    {   
    . reg = Wm8960_alc3, 
    . shift_l = 8, 
    . Shift_r = 8,
    . Max = 2, 
    . Texts = {"ALC", "Limiter"},
    . mask = xmax? Roundup_pow_of_two (xmax)-1:0/ /mask=?
    }

////////////////////////////////////////////////////////////////////////////

2. #define Soc_enum (xname, xenum) \
{   . iface = sndrv_ctl_elem_iface_mixer,. Name = xname,\
    . info = snd_soc_ Info_enum_double, \
    . get = snd_soc_get_enum_double,. put = snd_soc_put_enum_double, \
    . Private_value = ( unsigned long) &xenum}

ex:
static const struct Soc_enum WM8960_ENUM0 = Soc_enum_single (WM8960_ALC3, 8, 2, WM8 960_alcmode);

Soc_enum ("ALC Function", WM8960_ENUM0),
Similarly, after the expansion:

--->
    {   
    . iface = Sndrv_ctl_elem_iface_mixer, 
    . Name = "ADC polarity",
    . Info = snd_soc_info_enum_ Double, 
    . Get = Snd_soc_get_enum_double, 
    . put = snd_soc_put_enum_double, 
    . Private_value = (unsigned Long) &WM8960_ENUM0, 
    }

So, we found that the macro was actually a fill of a struct. Used to fill the structure of the snd_kcontrol_new.
Here's the incoming snd_soc_info_enum_double function:

int snd_soc_info_enum_double (struct Snd_kcontrol *kcontrol,
    struct snd_ctl_elem_info *uinfo)
{
    struct Soc_enum *e = (struct Soc_enum *) kcontrol->private_value;

    Uinfo->type = sndrv_ctl_elem_type_enumerated;
    Uinfo->count = e->shift_l = = E->shift_r? 1:2;
    Uinfo->value.enumerated.items = e->max;

    if (Uinfo->value.enumerated.item > e->max-1)
        uinfo->value.enumerated.item = e->max-1;
    strcpy (Uinfo->value.enumerated.name,
        E->texts[uinfo->value.enumerated.item]);
    return 0;
}

is to get some of the kcontrol->private_value parameters.

int snd_soc_get_enum_double (struct Snd_kcontrol *kcontrol,
    struct snd_ctl_elem_value *ucontrol)
{
    struct Snd_soc_codec *codec = snd_kcontrol_chip (Kcontrol);
    struct Soc_enum *e = (struct Soc_enum *) kcontrol->private_value;
    unsigned int val;

    val = Snd_soc_read (codec, e->reg);
    Ucontrol->value.enumerated.item[0]
        = (val >> e->shift_l) & e->mask;
    if (e->shift_l!= e->shift_r)
        ucontrol->value.enumerated.item[1] =
            (val >> e->shift_r) & e->mask;

    return 0;
}

is to read the state of some bit of the corresponding Kcontrol reg

int snd_soc_put_enum_double (struct Snd_kcontrol *kcontrol,
    struct snd_ctl_elem_value *ucontrol)
{
    struct Snd_soc_codec *codec = snd_kcontrol_chip (Kcontrol);
    struct Soc_enum *e = (struct Soc_enum *) kcontrol->private_value;
    unsigned int val;
    unsigned int mask;

    if (Ucontrol->value.enumerated.item[0] > E->max-1)
        return-einval;
    val = ucontrol->value.enumerated.item[0] << e->shift_l;
    Mask = e->mask << e->shift_l;
    if (e->shift_l!= e->shift_r) {
        if (ucontrol->value.enumerated.item[1] > E->max-1) return
            - einval;
        Val |= ucontrol->value.enumerated.item[1] << e->shift_r;
        Mask |= e->mask << e->shift_r;
    }

    Return snd_soc_update_bits_locked (codec, E->reg, Mask, Val);
}

By the same token, this is the state of some bit of the Reg that corresponds to Kcontrol.

struct Snd_kcontrol_new {
    snd_ctl_elem_iface_t iface;/* interface identifier
    /unsigned int device;        /* device/client number *
    /unsigned int subdevice;     /* Subdevice (substream) number */
    const unsigned char *name;  /* ASCII Name of item *
    /unsigned int index;     /* Index of item *
    /unsigned int access;        /* Access rights *
    /unsigned int count;     /* Count of same elements * *
    snd_kcontrol_info_t *info;
    snd_kcontrol_get_t *get;
    snd_kcontrol_put_t *put;
    Union {
        snd_kcontrol_tlv_rw_t *c;
        const unsigned int *p;
    } TLV;
    unsigned long private_value;
};

So, the macro is also the initialization action, and we can see that the
Soc_enum_single ()
Soc_enum ()--> needs to complete the soc_enum_xxx (), and of course the example above is the initialization of private data via Soc_enum_single ().
////////////////////////////////////////////////////////////////////////////
In essence, all macros are essentially services to implement the SND_KCONTROL_NEW structure, and the macros used to initialize snd_kcontrol_new are:

Soc_enum (XName, Xenum)
soc_dapm_single (XName, Reg, SHIFT, MAX, invert)
SOC_DOUBLE_R_TLV (XName, Reg_left, Reg_ Right, Xshift, Xmax, Xinvert, Tlv_array)
soc_double_r (XName, Reg_left, Reg_right, Xshift, Xmax, Xinvert)
SOC_ SINGLE_TLV (XName, Reg, SHIFT, Max, invert, Tlv_array)
soc_single (XName, Reg, SHIFT, MAX, invert)
Soc_single_ Bool_ext (XName, XData, Xhandler_get, Xhandler_put)

Dapm
Soc_dapm_single (XName, Reg, SHIFT, MAX, invert)

Therefore, we can know that these macros are used to initialize the snd_kcontrol_new structure, each macro corresponding to a particular register of the operation, can achieve read and write control. of which DAPM (digital audio power Maniger)
As an example of snd_kcontrol_new, snd_kcontrol_new structure is a very important data structure. It implements an instantiated operation of many registers
////////////////////////////////////////////////////////////////////////////

#define SND_SOC_DAPM_INPUT (wname) \
{   . id = snd_soc_dapm_input,. Name = Wname,. kcontrol_news = NULL, \
    . num_ Kcontrols = 0,. reg = snd_soc_nopm}
ex: 
snd_soc_dapm_input ("LINPUT1")   

Also unfold

--->
    {   
    . id = snd_soc_dapm_input, 
    . Name = "LINPUT1", 
    . Kcontrol_news = NULL,
    . Num_kcontrols = 0, 
    . reg = snd_soc_nopm 
    }
#define Snd_soc_dapm_mixer (Wname, Wreg, Wshift, Winvert, Wcontrols
     , wncontrols) \
{   . id = SND_SOC_DAPM _mixer,. Name = Wname,. reg = Wreg,. Shift = Wshift, \
    . Invert = Winvert,. kcontrol_news = Wcontrols,. Num_kcontrols = Wncontrols}

Ex
Snd_soc_dapm_mixer ("Left Input MIXER", Wm8960_power3, 5, 0,
Wm8960_lin, Array_size (Wm8960_lin)),
Expand

--->
    {   
    . id = snd_soc_dapm_mixer, 
    . Name = "Left Input mixer", 
    . reg = Wm8960_power3, 
    . Shift = 5,
  .invert = 0, 
    . kcontrol_news = Wm8960_lin, 
    . Num_kcontrols = Array_size (Wm8960_lin)
    }

We found that the two macros were also implemented to initialize a struct body.
There are differences in the member variables of the populated structure, depending on the macro.
The structural body is:

/* DAPM widget */struct Snd_soc_dapm_widget {enum Snd_soc_dapm_type ID;       const char *name;  /* Widget name */const char *sname;
    /* Stream name */struct SND_SOC_CODEC *codec;
    struct Snd_soc_platform *platform;
    struct List_head list;

    struct Snd_soc_dapm_context *dapm;             void *priv;        /* Widget specific data */struct regulator *regulator; /* attached regulator/const struct Snd_soc_pcm_stream *params;                /* Params for Dai links/////* DAPM control/INT reg;            /* Negative Reg = no direct DAPM */unsigned char shift;             /* BITS to shift */unsigned int value;          /* Widget Current value */unsigned int mask;            /* non-shifted Mask * * unsigned int on_val;           /* On State value */unsigned int off_val;          /* Off State value */unsigned char power:1;         /* Block Power status */unsigned char invert:1;
  /* Invert the power bit * *  unsigned char active:1;      /* Active stream on DAC, ADC ' s */unsigned char connected:1;            /* Connected codec pin/unsigned char new:1;            /* cnew Complete * * unsigned char ext:1;          /* has external widgets * * unsigned char force:1;         /* Force state */unsigned char ignore_suspend:1;      /* kept enabled over suspend * * unsigned char new_power:1;      /* Power from this run */unsigned char power_checked:1;             /* Power checked this run */int subseq;

    /* Sort within widget type */int (*power_check) (struct snd_soc_dapm_widget *w);     * External Events * * unsigned short event_flags;

    * Flags to specify event types */INT (*event) (struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);
    /* Kcontrols that relate to this widget */int num_kcontrols;
    const struct SND_KCONTROL_NEW *kcontrol_news;

    struct Snd_kcontrol **kcontrols; /* Widget input and outputs * * STRUCt list_head sources;

    struct List_head sinks;
    /* Used during DAPM updates * * struct list_head;
    struct List_head dirty;
    int inputs;

    int outputs;
struct CLK *clk; };

First of all, this is a DAPM-related thing that can probably be understood as a dedicated structure for digital audio power management widgets.
The structure contains a const struct snd_kcontrol_new *kcontrol_news;
That is to say, in some cases, in order to implement Dapm-widget, it is sometimes necessary to initialize the SND_KCONTROL_NEW data structure first.

The macros that are typically used to instantiate the DAPM widget are:

Snd_soc_dapm_input (wname)
snd_soc_dapm_supply (Wname, Wreg, Wshift, Winvert, Wevent, wflags)
SND_SOC_DAPM_ MIXER (Wname, Wreg, Wshift, Winvert,wcontrols, Wncontrols)
snd_soc_dapm_adc (Wname, Stname, Wreg, Wshift, Winvert)
Snd_soc_dapm_dac (Wname, Stname, Wreg, Wshift, Winvert)
SND_SOC_DAPM_PGA (Wname, Wreg, Wshift, Winvert,wcontrols, Wncontrols)
snd_soc_dapm_output (wname)

Once the DAPM widget is instantiated, we can control the switch of the Power module of a widget. As mentioned earlier, because of the power-saving relationship, it is necessary to add a separate DAPM widget to facilitate a separate module
For power control.
////////////////////////////////////////////////////////////////////////////

 * * DAPM audio route definition.
 *
 defines an audio route originating at source via control and finishing
 * at sink.
 * *
struct Snd_soc_dapm_route {
    const char *sink;
    const char *control;
    const char *source;

    /* note:currently only supported for links where the source is a supply/
    int (*connected) (struct Snd_soc_dapm_widget * Source,
             struct snd_soc_dapm_widget *sink);

By literal understanding, the role here is to implement a audio routing path.
Connect the sink side of the DAPM to the source DAPM so that the sound can be recorded/played
Ex
{"Left Boost Mixer", "LINPUT1 Switch", "LINPUT1"}
Significance: Connect the DAPM of the "left Boost Mixer" (sink end) to the dapm of the "LINPUT1" (source side). An action function that "LINPUT1 Switch" uses to provide control

When Wm8960_probe (struct Snd_soc_codec *codec), it does things:

{
    /*kcontrol: Control
    /Snd_soc_add_codec_controls (codec, Wm8960_snd_controls,
                     array_size (wm8960_snd_ controls));
    /*widgets: Component *
    /wm8960_add_widgets (codec);
}

Inside the static int wm8960_add_widgets (struct snd_soc_codec *codec)

{
    Snd_soc_dapm_new_controls (DAPM, Wm8960_dapm_widgets,
                  array_size (wm8960_dapm_widgets));

    Snd_soc_dapm_add_routes (DAPM, Audio_paths, Array_size (audio_paths));

Inside the wm8960_i2c_probe.
will have:

ret = Snd_soc_register_codec (&i2c->dev,
            &soc_codec_dev_wm8960, &wm8960_dai, 1);//Dai Num=1

In general, it can be said that:
First out of the book IIc Drive, when match to the corresponding device, execute the Iic_probe function, and finally use the SND_SOC_REGISTER_CODEC function to register codec.
When match to the corresponding device, carry out its own probe function, this function adds some control modules and DAPM components to the codec, and loads the DAPM routing table.
The entire codec drive function is the approximate process.

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.