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函数

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值