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