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