WM8903 codec driver 的詳解

                                                                                wm8903_codec_driver

 

source_code的路徑是在/kernel/sound/soc/codecs/wm88903.c裏面

 

driver的入口函數是:

staticint __init wm8903_modinit(void)

{

        return i2c_add_driver(&wm8903_i2c_driver);

}

module_init(wm8903_modinit);

 

wm8903_i2c_driver的值如下:

staticstruct i2c_driver wm8903_i2c_driver = {

        .driver = {

                .name = "WM8903",

                .owner = THIS_MODULE,

        },

        .probe    = wm8903_i2c_probe,

        .remove   = __devexit_p(wm8903_i2c_remove),

        .id_table = wm8903_i2c_id,

};

根據設備模型裏面的i2c bus 的匹配規則,匹配的原則是名字,當名字一旦相等的話,那麼就會調用probe函數

wm8903_i2c_probe函數

我們看下這個probe函數:

static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,

                                      conststruct i2c_device_id *id)

{

        struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev);

        struct wm8903_priv *wm8903;

        struct snd_soc_codec *codec;

        int ret;

        u16 val;

 

        wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);

        if (wm8903 == NULL)

                return -ENOMEM;

 

#ifdef CONFIG_I_LOVE_PBJ30            

        wm8903_dump = wm8903;

#endif

 

 

        codec = &wm8903->codec;

 

        mutex_init(&codec->mutex);

        INIT_LIST_HEAD(&codec->dapm_widgets);

        INIT_LIST_HEAD(&codec->dapm_paths);

 

        codec->dev = &i2c->dev;

        codec->name = "WM8903";

        codec->owner = THIS_MODULE;

        codec->bias_level = SND_SOC_BIAS_OFF;

        codec->set_bias_level = wm8903_set_bias_level;

        codec->dai = &wm8903_dai;

        codec->num_dai = 1;

        codec->reg_cache_size = ARRAY_SIZE(wm8903->reg_cache);

        codec->reg_cache = &wm8903->reg_cache[0];

        snd_soc_codec_set_drvdata(codec, wm8903);

        codec->volatile_register = wm8903_volatile_register;

        init_completion(&wm8903->wseq);

 

        i2c_set_clientdata(i2c, codec);

        codec->control_data = i2c;

 

        ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);

        if (ret != 0) {

                dev_err(&i2c->dev, "Failed to set cache I/O: %d\n", ret);

                goto err;

        }

 

        val = snd_soc_read(codec, WM8903_SW_RESET_AND_ID);

        if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {

                dev_err(&i2c->dev,

                        "Device with ID register %x is not a WM8903\n", val);

                return -ENODEV;

        }

 

        val = snd_soc_read(codec, WM8903_REVISION_NUMBER);

        dev_info(&i2c->dev, "WM8903 revision %d\n",

                 val & WM8903_CHIP_REV_MASK);

 

        wm8903_reset(codec);

 

 

#ifdef CONFIG_I_LOVE_PBJ30

        // workqueue initial

        INIT_WORK(&wm8903->work_hp, headphone_detect_work);

        INIT_WORK(&wm8903->work_dock_hp, dock_headphone_detect_work);  

 

        // Headphone detect irq thread

        if (1) {

                ret = request_threaded_irq(gpio_to_irq(HP_DET_GPIO), NULL, wm8903_hp_jack_handler,

                IRQF_DISABLED | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,

                                           "wm8903_HP_irq", wm8903);

 

                if (ret != 0) {

                        dev_err(&i2c->dev, "Failed to request HP IRQ: %d\n",

                                ret);

                        goto err;

                }

        }

 

        // Docking detect irq thread

        if (1) {

                ret = request_threaded_irq(gpio_to_irq(DOCK_HP_DET_GPIO), NULL, wm8903_dock_hp_jack_handler,

                IRQF_DISABLED | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,

                                           "wm8903_dock_HP_irq", wm8903);

 

                if (ret != 0) {

                        dev_err(&i2c->dev, "Failed to request HP IRQ: %d\n",

                                ret);

                        goto err;

                }

        }

       

 

        // Docking ON device node

        Dock_ON_kobj = kobject_create_and_add("DOCK_ON", NULL);

        if (Dock_ON_kobj == NULL) {

                printk("%s: subsystem_register failed\n", __FUNCTION__);

        }

   

    ret = sysfs_create_group(Dock_ON_kobj, &attribute_group);

        if(ret) {

                printk("%s: sysfs_create_group failed, %d\n", __FUNCTION__, __LINE__);

        }              

#endif

 

 

        /* power on device */

        wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

 

 

        /* Latch volume update bits */

        val = snd_soc_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT);

        val |= WM8903_ADCVU;

        snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val);

        snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val);

 

        val = snd_soc_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT);

        val |= WM8903_DACVU;

        snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val);

        snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val);

 

        val = snd_soc_read(codec, WM8903_ANALOGUE_OUT1_LEFT);

        val |= WM8903_HPOUTVU;

        snd_soc_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val);

        snd_soc_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val);

 

        val = snd_soc_read(codec, WM8903_ANALOGUE_OUT2_LEFT);

        val |= WM8903_LINEOUTVU;

        snd_soc_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val);

        snd_soc_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val);

 

        val = snd_soc_read(codec, WM8903_ANALOGUE_OUT3_LEFT);

        val |= WM8903_SPKVU;

        snd_soc_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val);

        snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);

 

        /* Enable DAC soft mute by default */

        val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);

        val |= WM8903_DAC_MUTEMODE;

        snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val);

 

        wm8903_dai.dev = &i2c->dev;

        wm8903_codec = codec;

 

        ret = snd_soc_register_codec(codec);

        if (ret != 0) {

                dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);

                goto err_irq;

        }

 

        ret = snd_soc_register_dai(&wm8903_dai);

        if (ret != 0) {

                dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);

                goto err_codec;

        }

#if defined(CONFIG_I_LOVE_PBJ20) || defined(CONFIG_I_LOVE_PBJ30)

 

        INIT_WORK(&wm8903->work, wm8903_amp_work);

        wm8903->amp_enable = 0;

        wm8903->amp_status = 0;

        wm8903->amp_event  = 0;

 

#endif

        return ret;

 

err_codec:

        snd_soc_unregister_codec(codec);

err_irq:

        if (i2c->irq)

                free_irq(i2c->irq, wm8903);

err:

        wm8903_codec = NULL;

        kfree(wm8903);

        return ret;

}

上面的代碼比較長,但是慢慢的去分析。

代碼的前面的部分直到ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);部分,這裏都是在struct snd_soc_codec codec成員進行賦值的初始化的工作。這裏就不細說了。主要是看下面的代碼。

 

首先是:ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);

/**

 * snd_soc_codec_set_cache_io: Set up standard I/O functions.

 *

 * @codec: CODEC to configure.

 * @type: Type of cache.

 * @addr_bits: Number of bits of register address data.

 * @data_bits: Number of bits of data per register.

 * @control: Control bus used.

 *

 * Register formats are frequently shared between many I2C and SPI

 * devices.  In order to promote code reuse the ASoC core provides

 * some standard implementations of CODEC read and write operations

 * which can be set up using this function.

 *

 * The caller is responsible for allocating and initialising the

 * actual cache.

 *

 * Note that at present this code cannot be used by CODECs with

 * volatile registers.

 */

int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,

                               int addr_bits, int data_bits,

                               enum snd_soc_control_type control)

{

        int i;

 

        for (i = 0; i < ARRAY_SIZE(io_types); i++)

                if (io_types[i].addr_bits == addr_bits &&

                    io_types[i].data_bits == data_bits)

                        break;

        if (i == ARRAY_SIZE(io_types)) {

                printk(KERN_ERR

                       "No I/O functions for %d bit address %d bit data\n",

                       addr_bits, data_bits);

                return -EINVAL;

        }

 

        codec->write = io_types[i].write;

        codec->read = io_types[i].read;

 

        switch (control) {

        caseSND_SOC_CUSTOM:

                break;

 

        caseSND_SOC_I2C:

#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))

                codec->hw_write = (hw_write_t)i2c_master_send;

#endif

                if (io_types[i].i2c_read)

                        codec->hw_read = io_types[i].i2c_read;

                break;

 

        caseSND_SOC_SPI:

                if (io_types[i].spi_write)

                        codec->hw_write = io_types[i].spi_write;

                break;

        }

 

        return 0;

}

EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);

上面的snd_soc_codec_set_cache_io Function主要是根據我們設置的codec 的register 的addr_bits、每一個register的bits數目,以及控制的總線的類型: SND_SOC_I2C,根據以上的三種條件進行選擇的爲我們所配置的codec賦值io讀寫函數

1、根據addr_bits和data_bits兩個共同選擇出codec的IO讀寫函數

2、根據我們的codec的所對應控制總線的類型,選擇出這個總線所對應的控制讀寫函數

codec 的讀寫函數賦值:

        codec->write = io_types[i].write;

        codec->read = io_types[i].read;

codec的控制讀寫函數賦值:

codec->hw_write = (hw_write_t)i2c_master_send;

codec->hw_read = io_types[i].i2c_read;

 

 

1、接下來是:

val = snd_soc_read(codec, WM8903_SW_RESET_AND_ID);

#define WM8903_SW_RESET_AND_ID                  0x00

這裏是讀取這個device 的device ID ,通過查詢wm8903的datasheet可以查詢到讀取這個register 將會返回Device ID 8903H

我們默認的寄存器裏面的值也是8903H的,只有相等才說明這個芯片是我們想要的芯片

 

2、val = snd_soc_read(codec, WM8903_REVISION_NUMBER);

#define WM8903_REVISION_NUMBER                  0x01

讀取0x01這個寄存器查詢datasheet後,這個register readonly register讀取這個寄存器將返回version ID

 

3、將codec reset :wm8903_reset(codec);

staticvoid wm8903_reset(struct snd_soc_codec *codec)

{

        snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0);

        memcpy(codec->reg_cache, wm8903_reg_defaults,

               sizeof(wm8903_reg_defaults));

}

這個函數是在向R0 寄存器寫值,根據我們查詢datasheet後,發現writing to this register resets all registers to their default state

 

4、下面申請兩個中斷處理函數:

4.1:Headphone detect irq thread

 

INIT_WORK(&wm8903->work_hp, headphone_detect_work);

 

ret = request_threaded_irq(gpio_to_irq(HP_DET_GPIO), NULL, wm8903_hp_jack_handler,IRQF_DISABLED | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "wm8903_HP_irq", wm8903);

 

中斷處理函數的原型:

staticirqreturn_t wm8903_hp_jack_handler(int irq, void *dev_id)

{

       

        struct wm8903_priv *wm8903 = (struct wm8903_priv *)dev_id;

    schedule_work(&wm8903->work_hp);

        returnIRQ_HANDLED;

}

也就是調用了 headphone_detect_work

staticvoid headphone_detect_work(struct work_struct *work)

{

    struct wm8903_priv *wm8903 =

            container_of(work, struct wm8903_priv, work_hp);  

        int report;

        int int_pol;

        struct wm8903_jack_data *jack = NULL;

 

        jack = &wm8903->hp;

 

        msleep(100);

 

        if (!jack->jack) {

                printk("Jack interrupt called with no jack\n");

        }

        int_pol = gpio_get_value(HP_DET_GPIO);

 

        if (!int_pol) {

                /* set tablet headphone event */

                wm8903->hp_state = 1;

 

                /* detect mic state when headphone plug in */

                mic_det_export();

               

                /* set audio switch to tablet */

                /* only for pbj30 dvt version */

                gpio_set_value(TEGRA_GPIO_PD0, 1);

               

                /* report JACK state to alsa api */

                report = SND_JACK_HEADPHONE;

               

        } else {

                /* plug out headphong or headset,all set to int mic */

                MicSwitch_int();

                /* clear tablet headphone event */

                wm8903->hp_state = 0;

               

                /* set audio switch to docking */

                /* only for pbj30 dvt version */

                gpio_set_value(TEGRA_GPIO_PD0, 0);

 

#if 0          

                // if dock on should be enable headphone too

                if(wm8903->docking_state)

                report = SND_JACK_LINEOUT | SND_JACK_HEADPHONE;

                else

                report = SND_JACK_HEADPHONE;

#endif

               

                printk("Headphone removed\n");

               

        }

       

        /* update headphone event to frameworks */

        if(wm8903->docking_state)

        headphone_event(wm8903->docking_hp_state || wm8903->hp_state);

        else

        headphone_event(wm8903->hp_state);

       

#if 0  

        snd_soc_jack_report(jack->jack, report, jack->report);           

#endif

}

敘述上面的代碼,獲得HP_DET_GPIO這個GPIO的值,根據電平的高低來判斷耳機有的插入

如果是低電平的話,那麼就爲平板設置headphone event

設置wm8903->hp_state=1

 

調用mic_det_export函數//detec mic state when headphone plug in

mic_det_export:

 

 

void mic_det_export(void)

{

        int int_pol = 0;

        int mic_status = 0,mic_count=0,i;

 

        /* Mic detect initial */

        snd_soc_write(&wm8903_dump->codec, WM8903_MIC_BIAS_CONTROL_0, 0x17);

        snd_soc_write(&wm8903_dump->codec, WM8903_CLOCK_RATES_2, (WM8903_CLK_SYS_ENA | WM8903_CLK_DSP_ENA));

 

        int_pol = gpio_get_value(HP_DET_GPIO);

        /* polling mic detect & short interrupt status */

        if(!int_pol){

                for(i=1;i<=5;i++){

                        snd_soc_update_bits(&wm8903_dump->codec, WM8903_INTERRUPT_POLARITY_1,

                        (WM8903_MICDET_INV | WM8903_MICSHRT_INV),(WM8903_MICDET_INV | WM8903_MICSHRT_INV));

                        msleep(5);

                        mic_status = snd_soc_read(&wm8903_dump->codec, WM8903_INTERRUPT_STATUS_1);

                        snd_soc_update_bits(&wm8903_dump->codec, WM8903_INTERRUPT_POLARITY_1,

                        (WM8903_MICDET_INV | WM8903_MICSHRT_INV),~(WM8903_MICDET_INV | WM8903_MICSHRT_INV));

                        msleep(5);

                        mic_status = ( snd_soc_read(&wm8903_dump->codec, WM8903_INTERRUPT_STATUS_1) & 0xf000 ) >> 12;

                        if(mic_status & 0xc)

                        if(++mic_count > 3)

                        break;

                }

               

                if(mic_count > 3){

                        printk("Headphone inserted\n");

                        MicSwitch_int();

                }else{

                        printk("Headset inserted\n");

                        MicSwitch_ext();                       

                }

        }else{

                        MicSwitch_int();

                }

}

EXPORT_SYMBOL_GPL(mic_det_export);

 

敘述上面的函數:

/* Mic detect initial */

        snd_soc_write(&wm8903_dump->codec, WM8903_MIC_BIAS_CONTROL_0, 0x17);

        snd_soc_write(&wm8903_dump->codec, WM8903_CLOCK_RATES_2, (WM8903_CLK_SYS_ENA | WM8903_CLK_DSP_ENA));

上面的兩個函數是在向codec的寄存器裏面進行寫值

#define WM8903_MIC_BIAS_CONTROL_0               0x06

#define WM8903_CLOCK_RATES_2                    0x16

0x06 寄存器裏面  寫入0x17  對應的二進制就是  0001 0001

看下這個寄存器所對應的datasheet裏面是如何寫的:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

對應上面的每一位,看下設置的作用

接下來看下往0x16寄存器裏面寫

(WM8903_CLK_SYS_ENA | WM8903_CLK_DSP_ENA)

#define WM8903_CLK_SYS_ENA                      0x0004  /* CLK_SYS_ENA */    0100

#define WM8903_CLK_DSP_ENA                      0x0002  /* CLK_DSP_ENA */    0010

也就是向0x16寄存器裏面寫入 110

查詢下datasheet後:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

下面會再次調用gpio_get_value(HP_DET_GPIO)函數是爲了確保Headphone有沒有被拔出,如果沒有拔出的話,那麼下面就會操作相關的寄存器。

 

snd_soc_update_bits(&wm8903_dump->codec, WM8903_INTERRUPT_POLARITY_1,(WM8903_MICDET_INV | WM8903_MICSHRT_INV),(WM8903_MICDET_INV | WM8903_MICSHRT_INV));

 

reg_value:

#define WM8903_INTERRUPT_POLARITY_1             0x7B

#define WM8903_INTERRUPT_STATUS_1               0x79

我們首先操作的寄存器是 0x7B,通過調用snd_soc_update_bits函數

 

 

 

 

 

 

 

 

接下來調用snd_soc_read函數進行讀取0x79 寄存器的status ,通過這個status 的值來判斷我們有沒有mic_phone,檢測一次還是不能確定有沒有mic_Phone,當超過三次以後,那麼才能確認有沒有mic_phone,當然這些只是大概的估計,爲了保險,當IRQ沒有產生的話,那麼就不會是mic_phone

看下0x79寄存器的前面兩位是什麼意思?因爲我們最終看的也是前面兩位

 

 

 

 

 

 

 

 

 

如果是Mic_phone的話,那麼就會執行

MicSwitch_int();

否則就會執行:

MicSwitch_ext();       

 

上面的函數的實現如下:

#ifdef CONFIG_I_LOVE_PBJ30

void MicSwitch_int(void) {

 

        i2c_smbus_write_word_data(EC_Bat_device->client,0x44,0);

        msleep(100);

}

SYMBOL_EXPORT(MicSwitch_int);

 

void MicSwitch_ext(void) {

 

        i2c_smbus_write_word_data(EC_Bat_device->client,0x44,1);

        msleep(100);

}

SYMBOL_EXPORT(MicSwitch_ext);

#endif

上面是在通過smbus 向EC battery device 進行發出cmd

當有docking 或者耳機插入的時候,那麼相應的wm8903 的docking_hp_state和hp_state的狀態也會發生改變,我們需要及時的回報這種event到frameworks層.

通過調用headphone_event(wm8903->hp_state);進行上報

 

接下來又有一箇中斷處理函數:

 

request_threaded_irq(gpio_to_irq(DOCK_HP_DET_GPIO), NULL, wm8903_dock_hp_jack_handler,

                IRQF_DISABLED | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,

                                           "wm8903_dock_HP_irq", wm8903);

上面的中斷處理函數和上面的其實是一樣的,只是以前的pbj30有一個外接的docking ,這個docking 上面有一個插口可以用來插耳機

下面會進行DOCK的屬性文件的創建的等一些工作

 

 

接下來執行:

wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

codec沒有在playback 或者capture的時候,那麼就設置成這種standby 狀態

 

staticint wm8903_set_bias_level(struct snd_soc_codec *codec,

                                 enum snd_soc_bias_level level)

{

switch (level) {

…..............

…..............

        caseSND_SOC_BIAS_STANDBY:

                if (codec->bias_level == SND_SOC_BIAS_OFF) {

                        snd_soc_write(codec, WM8903_CLOCK_RATES_2,

                                     WM8903_CLK_SYS_ENA);

 

                        /* Change DC servo dither level in startup sequence */

                        snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11);

                        snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257);

                        snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2);

 

                        wm8903_run_sequence(codec, 0);

                        wm8903_sync_reg_cache(codec, codec->reg_cache);

 

                        /* Enable low impedence charge pump output */

                        reg = snd_soc_read(codec,

                                          WM8903_CONTROL_INTERFACE_TEST_1);

                        snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,

                                     reg | WM8903_TEST_KEY);

                        reg2 = snd_soc_read(codec, WM8903_CHARGE_PUMP_TEST_1);

                        snd_soc_write(codec, WM8903_CHARGE_PUMP_TEST_1,

                                     reg2 | WM8903_CP_SW_KELVIN_MODE_MASK);

                        snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,

                                     reg);

 

                        /* By default no bypass paths are enabled so

                         * enable Class W support.

                         */

                        dev_dbg(&i2c->dev, "Enabling Class W\n");

                        snd_soc_write(codec, WM8903_CLASS_W_0, reg |

                                     WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);

                }

 

                reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);

                reg &= ~(WM8903_VMID_RES_MASK);

                reg |= WM8903_VMID_RES_250K;

                snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);

                break;

 

….....................

….....................

return 0;

}

 

我們會判斷codec->bias_level的值是不是SND_SOC_BIAS_OFF,如果是的話,需要調用以下函數對register進行處理

1:snd_soc_write(codec, WM8903_CLOCK_RATES_2,WM8903_CLK_SYS_ENA);

 

只使能system clock ,關閉 DSP clock 和Zero cross time

 

2:change DC servo gither level in startup sequence操作音序器

regiter:6C、6D、6E

snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11);

snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257);

snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2);

上面的函數都是在控制音序寄存器

上面查看下datasheet就知道在幹嘛了,控制過音序器以後,就會調用

 

wm8903_run_sequence(codec, 0);//

1、如果這個音序器還沒有起來的話,那麼就再次的使能它

2、如果使能成功的話,那麼就直接disable 音序器

 

3:Enable low impedence charge pump output 低阻抗輸出

FLL control Frequency Locked LOOP 鎖頻環的控制

 

reg = snd_soc_read(codec, WM8903_CONTROL_INTERFACE_TEST_1);

snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,reg | WM8903_TEST_KEY);

reg2 = snd_soc_read(codec, WM8903_CHARGE_PUMP_TEST_1);snd_soc_write(codec, WM8903_CHARGE_PUMP_TEST_1,

reg2 | WM8903_CP_SW_KELVIN_MODE_MASK);

snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,reg);

上面在控制0x81和0x95這兩個寄存器。81寄存器是在控制鎖頻環,95這個寄存器我在datasheet中沒有找到

 

register:

0x68 :enable dynamic charge pump power control

0x05:控制VMID 解碼和音頻電源

在代碼中

snd_soc_write(codec, WM8903_CLASS_W_0, reg |WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);

reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);

reg &= ~(WM8903_VMID_RES_MASK);

reg |= WM8903_VMID_RES_250K;

snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);

 

下面是在操作它的音量的寄存器

register :

0x24

0x25

0x1E

0x1F

0x39

0x3A

0x3B

0x3C

0x3E

0x3F

上面是在控左右DAC ADC的聲道的音量

 

 

register:

0x21

設置DAC soft mute的值是1

val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);

val |= WM8903_DAC_MUTEMODE;

snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val);

 

接下來會會進行我們codec和codec dai 的註冊

 

register codec:

/**

 * snd_soc_register_codec - Register a codec with the ASoC core

 *

 * @codec: codec to register

 */

int snd_soc_register_codec(struct snd_soc_codec *codec)

{

        int i;

 

        if (!codec->name)

                return -EINVAL;

 

        /* The device should become mandatory over time */

        if (!codec->dev)

                printk(KERN_WARNING "No device for codec %s\n", codec->name);

 

        INIT_LIST_HEAD(&codec->list);

 

        for (i = 0; i < codec->num_dai; i++) {

                fixup_codec_formats(&codec->dai[i].playback);

                fixup_codec_formats(&codec->dai[i].capture);

        }

 

        mutex_lock(&client_mutex);

        list_add(&codec->list, &codec_list);

        snd_soc_instantiate_cards();

        mutex_unlock(&client_mutex);

 

        pr_debug("Registered codec '%s'\n", codec->name);

 

        return 0;

}

EXPORT_SYMBOL_GPL(snd_soc_register_codec);

1、上面首先會修正了codec的端點的格式

2、將我們註冊的codec添加到一個靜態鏈表中去,static LIST_HEAD(codec_list);這裏需要注意的是這裏是一條靜態鏈表,將codec添加到這條鏈表中方便尋找,接下來我們也會看到類似的靜態鏈表的

3、snd_soc_instantiate_cards();初始化聲卡設備

 

看下這個snd_soc_instantiate_cards:

/*

 * Attempt to initialise any uninitialised cards.  Must be called with

 * client_mutex.

 */

staticvoid snd_soc_instantiate_cards(void)

{

        struct snd_soc_card *card;

        list_for_each_entry(card, &card_list, list)

                snd_soc_instantiate_card(card);

}

最終會遍歷card_list這個鏈表中的所有的聲卡設備,然後調用snd_soc_instantiate函數進行這個聲卡中的所有component的初始化

 

 

下面:dai 的register,數字音頻接口

ret = snd_soc_register_dai(&wm8903_dai);

看下注冊dai 的函數的實現:

wm8903_dai的值如下:

struct snd_soc_dai wm8903_dai = {

        .name = "WM8903",

        .playback = {

                .stream_name = "Playback",

                .channels_min = 2,

                .channels_max = 2,

                .rates = WM8903_PLAYBACK_RATES,

                .formats = WM8903_FORMATS,

        },

        .capture = {

                 .stream_name = "Capture",

                 .channels_min = 2,

                 .channels_max = 2,

                 .rates = WM8903_CAPTURE_RATES,

                 .formats = WM8903_FORMATS,

         },

        .ops = &wm8903_dai_ops,

        .symmetric_rates = 1,

};

EXPORT_SYMBOL_GPL(wm8903_dai);

上面的結構裏面有playback :播放音樂

capture:錄音的

capture和playback 裏面都對應的name、channel、還有操作集合

snd_soc_register_dai:

 

/**

 * snd_soc_register_dai - Register a DAI with the ASoC core

 *

 * @dai: DAI to register

 */

int snd_soc_register_dai(struct snd_soc_dai *dai)

{

        if (!dai->name)

                return -EINVAL;

 

        /* The device should become mandatory over time */

        if (!dai->dev)

                printk(KERN_WARNING "No device for DAI %s\n", dai->name);

 

        if (!dai->ops)

                dai->ops = &null_dai_ops;

 

        INIT_LIST_HEAD(&dai->list);

 

        mutex_lock(&client_mutex);

        list_add(&dai->list, &dai_list);

        snd_soc_instantiate_cards();

        mutex_unlock(&client_mutex);

 

        pr_debug("Registered DAI '%s'\n", dai->name);

 

        return 0;

}

EXPORT_SYMBOL_GPL(snd_soc_register_dai);

dai設備依然添加到一個靜態的鏈表中去和註冊codec的方法是一樣的

list_add(&dai->list, &dai_list);

snd_soc_instantiate_cards();

dai 添加到dai_list鏈表中去

 

下面爲wm8903裏面的成員賦值

 

到這裏codec的驅動的入口函數:wm8903_i2c_probe函數就講解完了,在接下來會詳細講解snd_soc_instantiate_card函數

發佈了18 篇原創文章 · 獲贊 11 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章