linux驅動開發: wm8960 codec程式碼分析__linux

來源:互聯網
上載者:User

關於alsa架構已經啃了好久好久,但是也卡了好久好久。難說皮毛到底有看懂多少,不管,我們先來啃wm8960 codec的驅動代碼:

必要相關函數說明:
////////////////////////////////////////////////////////////////////////////

1.#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \    SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts)#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \{   .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \    .max = xmax, .texts = xtexts, \    .mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0}ex:#define WM8960_ALC3     0x13static const char *wm8960_alcmode[] = {"ALC", "Limiter"};SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode)

這個宏定義的作用:reg 0x13的bit8是的功能是select alc mode,
設定0:alc mode 1:limite mode.
1.大概猜測:
SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts)
Defines an 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 that describe each setting

函數的作用:
用這個define去填充某個特殊的結構體,從而實現相應的初始化設定:

 /* 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;};

根據理解,我們發現SOC_ENUM_SINGLE()這個宏用來填充soc_enum的結構體,如果將它展開:

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, wm8960_alcmode);

SOC_ENUM(“ALC Function”, wm8960_enum0),
同樣,展開後:

--->    {       .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,     }

所以,我們發現,這個宏實際上還是對某個結構體的填充.用來填充snd_kcontrol_new這個結構體
這邊傳入的snd_soc_info_enum_double這個函數:

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;}

是為了獲得kcontrol->private_value的某些參數

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;}

是為了讀取對應的kcontrol的reg的某些bit的狀態

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);}

同理,這邊是設定對應kcontrol的reg的某些bit的狀態.

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;};

所以,這個宏同樣是完成初始化動作,而且我們可以看到,
SOC_ENUM_SINGLE()
SOC_ENUM()—->需要先完成SOC_ENUM_XXX(),當然上面的例子是通過SOC_ENUM_SINGLE()來實現private data的初始化的
////////////////////////////////////////////////////////////////////////////
本質上,所有的宏基本上都是為了實現snd_kcontrol_new的結構體而服務,用來初始化snd_kcontrol_new的宏有:

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)

所以,我們可以知道,這些宏都是用來初始化snd_kcontrol_new結構體的,每個宏對應某個寄存器的特殊操作,可以實現讀寫控制。其中DAPM(digital audio power maniger)
也算snd_kcontrol_new的一種執行個體,snd_kcontrol_new結構是一種很重要的資料結構。它實現了許多寄存器的執行個體化的操作
////////////////////////////////////////////////////////////////////////////

#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")   

同樣展開

--->    {       .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)),
展開:

--->    {       .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)    }

我們發現這兩個宏,同樣是實現了對某個結構體的初始化.
只是因宏的不同,填充的結構體的成員變數會有差異.
這個結構體是:

/* 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 power_list;    struct list_head dirty;    int inputs;    int outputs;    struct clk *clk;};

首先這個是DAPM相關的東西,大概可以理解為 數位聲音電源管理小組件專用的結構體.
這個結構體裡面有包含const struct snd_kcontrol_new *kcontrol_news;
也就是說在某些情況下,為了實現DAPM-widget,有時需要先初始化snd_kcontrol_new資料結構.

通常用來執行個體化DAPM widget的宏有:

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)

DAPM widget的執行個體化後,我們便可以控制某個小組件的電源模組的開關.前面有說過,因為省電的關係,需要單獨增加DAPM widget的電源小組件,以方便獨立對某個模組
進行電源控制.
////////////////////////////////////////////////////////////////////////////

/* * 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 source is a supply */    int (*connected)(struct snd_soc_dapm_widget *source,             struct snd_soc_dapm_widget *sink);};

根據字面理解,這邊的作用是實現一條audio路由通路。
將sink端的DAPM和source的DAPM串連起來,這樣便可以進行聲音的錄製/播放
ex:
{ “Left Boost Mixer”, “LINPUT1 Switch”, “LINPUT1” }
意義:將 “Left Boost Mixer”(sink端)的DAPM與 “LINPUT1”(source端)的DAPM進行一次connect. “LINPUT1 Switch”用來提供control的操作函數

當wm8960_probe(struct snd_soc_codec *codec)時,它做的事情:

{    /*kcontrol: 控制項*/    snd_soc_add_codec_controls(codec, wm8960_snd_controls,                     ARRAY_SIZE(wm8960_snd_controls));    /*widgets: 組件*/    wm8960_add_widgets(codec);}

在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));}

在wm8960_i2c_probe裡面
會有:

ret = snd_soc_register_codec(&i2c->dev,            &soc_codec_dev_wm8960, &wm8960_dai, 1);// dai num=1

大致上可以這樣認為:
首先出冊IIC驅動,當match到對應裝置時,執行iic_probe函數,同時最後再使用snd_soc_register_codec函數註冊codec。
當match到對應的裝置時,進行它自己的probe函數,此函數會向codec中添加一些控制小模組和DAPM的組件,同時載入DAPM路由表.
整個codec的驅動函數大致流程就是這樣

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.