linux系統(tǒng)默認的耳機插拔檢測驅動程序被整合在聲卡驅動中,這使得耳機插拔狀態(tài)能夠通過輸入子系統(tǒng)進行報告。
這一功能的具體實現(xiàn)位于kernel-5.15版本的sound/soc/Generic/simple-card-utils.c文件中。
571??int?asoc_simple_init_jack(struct?snd_soc_card?*card, 572???????struct?asoc_simple_jack?*sjack, 573???????int?is_hp,?char?*prefix, 574???????char?*pin) 575??{ 576???struct?device?*dev?=?card->dev; 577???enum?of_gpio_flags?flags; 578???char?prop[128]; 579???char?*pin_name; 580???char?*gpio_name; 581???int?mask; 582???int?det; 583?? 584???if?(!prefix) 585????prefix?=?""; 586?? 587???sjack->gpio.gpio?=?-ENOENT; 588?? 589???if?(is_hp)?{ 590????snprintf(prop,?sizeof(prop),?"%shp-det-gpio",?prefix); 591????pin_name?=?pin???pin?:?"Headphones"; 592????gpio_name?=?"Headphone?detection"; 593????mask??=?SND_JACK_HEADPHONE; 594???}?else?{ 595????snprintf(prop,?sizeof(prop),?"%smic-det-gpio",?prefix); 596????pin_name?=?pin???pin?:?"Mic?Jack"; 597????gpio_name?=?"Mic?detection"; 598????mask??=?SND_JACK_MICROPHONE; 599???} 600?? 601???det?=?of_get_named_gpio_flags(dev->of_node,?prop,?0,?&flags); 602???if?(det?==?-EPROBE_DEFER) 603????return?-EPROBE_DEFER; 604?? 605???if?(gpio_is_valid(det))?{ 606????sjack->pin.pin??=?pin_name; 607????sjack->pin.mask??=?mask; 608?? 609????sjack->gpio.name?=?gpio_name; 610????sjack->gpio.report?=?mask; 611????sjack->gpio.gpio?=?det; 612????sjack->gpio.invert?=?!!(flags?&?OF_GPIO_ACTIVE_LOW); 613????sjack->gpio.debounce_time?=?150; 614?? 615????snd_soc_card_jack_new(card,?pin_name,?mask, 616????????????&sjack->jack, 617????????????&sjack->pin,?1); 618?? 619????snd_soc_jack_add_gpios(&sjack->jack,?1, 620?????????????&sjack->gpio); 621???} 622?? 623???return?0; 624??} 625??EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
第 589~593 行,【構建】用于查找設備樹中 GPIO 屬性的屬性名稱 prop。設置 pin_name 為”Headphones”,表示插孔的名稱。設置 gpio_name 為 “Headphone detection”,表示 GPIO 的名稱。設置 mask 為 SND_JACK_HEADPHONE,表示這是一個耳機插孔。
第 601 行,使用設備樹函數(shù) of_get_named_gpio_flags 獲取與屬性名稱 prop 關聯(lián)的 GPIO 描述符,并存儲在 det 中。如果 GPIO 未定義,det 將為負數(shù)。
第 606~613 行,如果設置了檢測 GPIO,那么設置結構體指針 sjack 的一些屬性。設置插孔的引腳信息,比如 sjack->pin.pin 引腳名字。設置耳機插孔的一些 GPIO 關聯(lián)信息,如 sjack->gpio.gpio 是表示 GPIO 描述符,sjack->gpio.invert 表示根據(jù)設備樹中的屬性決定是否反轉 GPIO 狀態(tài),GPIO_ACTIVE_LOW 是低電平表示活動,當耳機插入時,檢測腳將被拉低,說明是低有效。debounce_time 這個是設置消抖時間,防止誤檢測。
第 615 行,這里將耳機插孔與聲卡綁定。
第 619 行,綁定 GPIO,就會觸發(fā)耳機插撥事件。
函數(shù)名重定義:/include/sound/simple_card_utils.h
14??#define?asoc_simple_init_hp(card,?sjack,?prefix)? 15???asoc_simple_init_jack(card,?sjack,?1,?prefix,?NULL) 16??#define?asoc_simple_init_mic(card,?sjack,?prefix)? 17???asoc_simple_init_jack(card,?sjack,?0,?prefix,?NULL)
在聲卡驅動 probe 時調(diào)用
dts 中配置聲卡節(jié)點 compatible = “simple-audio-card”
734??static?const?struct?of_device_id?simple_of_match[]?=?{ 735???{?.compatible?=?"simple-audio-card",?}, 736???{?.compatible?=?"simple-scu-audio-card", 737?????.data?=?(void?*)DPCM_SELECTABLE?}, 738???{}, 739??}; 740??MODULE_DEVICE_TABLE(of,?simple_of_match); 614??static?int?simple_soc_probe(struct?snd_soc_card?*card) 615??{ 616???struct?asoc_simple_priv?*priv?=?snd_soc_card_get_drvdata(card); 617???int?ret; 618?? 619???ret?=?asoc_simple_init_hp(card,?&priv->hp_jack,?PREFIX); 620???if?(ret?return?ret; 622?? 623???ret?=?asoc_simple_init_mic(card,?&priv->mic_jack,?PREFIX); 624???if?(ret?return?ret; 626?? 627???return?0; 628??}
這個驅動文件負責聲卡的初始化,音頻流管理,控制接口等。在第 619 行,調(diào)用了耳機檢測 IO 初始化的代碼。
耳機拔插上報 flow
asoc_simple_init_jack 會調(diào)用 snd_soc_card_jack_new,添加檢測管腳 pins,進而一路調(diào)用下來
60??int?snd_soc_card_jack_new(struct?snd_soc_card?*card,?const?char?*id,?int?type, 61???????struct?snd_soc_jack?*jack, 62???????struct?snd_soc_jack_pin?*pins,?unsigned?int?num_pins) 63??{ 64???int?ret; 65?? 66???mutex_init(&jack->mutex); 67???jack->card?=?card; 68???INIT_LIST_HEAD(&jack->pins); 69???INIT_LIST_HEAD(&jack->jack_zones); 70???BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); 71?? 72???ret?=?snd_jack_new(card->snd_card,?id,?type,?&jack->jack,?false,?false); 73???if?(ret) 74????goto?end; 75?? 76???if?(num_pins) 77????ret?=?snd_soc_jack_add_pins(jack,?num_pins,?pins); 78??end: 79???return?soc_card_ret(card,?ret); 80??} 81??EXPORT_SYMBOL_GPL(snd_soc_card_jack_new); 137??int?snd_soc_jack_add_pins(struct?snd_soc_jack?*jack,?int?count, 138???????struct?snd_soc_jack_pin?*pins) 139??{ 140???int?i; 141?? 142???for?(i?=?0;?i?if?(!pins[i].pin)?{ 144?????dev_err(jack->card->dev,?"ASoC:?No?name?for?pin?%d ", 145??????i); 146?????return?-EINVAL; 147????} 148????if?(!pins[i].mask)?{ 149?????dev_err(jack->card->dev,?"ASoC:?No?mask?for?pin?%d" 150??????"?(%s) ",?i,?pins[i].pin); 151?????return?-EINVAL; 152????} 153?? 154????INIT_LIST_HEAD(&pins[i].list); 155????list_add(&(pins[i].list),?&jack->pins); 156????snd_jack_add_new_kctl(jack->jack,?pins[i].pin,?pins[i].mask); 157???} 158?? 159???/*?Update?to?reflect?the?last?reported?status;?canned?jack 160????*?implementations?are?likely?to?set?their?state?before?the 161????*?card?has?an?opportunity?to?associate?pins. 162????*/ 163???snd_soc_jack_report(jack,?0,?0); 164?? 165???return?0; 166??} 167??EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins); 34??void?snd_soc_jack_report(struct?snd_soc_jack?*jack,?int?status,?int?mask) 35??{ 36???struct?snd_soc_dapm_context?*dapm; 37???struct?snd_soc_jack_pin?*pin; 38???unsigned?int?sync?=?0; 39?? 40???if?(!jack) 41????return; 42???trace_snd_soc_jack_report(jack,?mask,?status); 43?? 44???dapm?=?&jack->card->dapm; 45?? 46???mutex_lock(&jack->mutex); 47?? 48???jack->status?&=?~mask; 49???jack->status?|=?status?&?mask; 50?? 51???trace_snd_soc_jack_notify(jack,?status); 52?? 53???list_for_each_entry(pin,?&jack->pins,?list)?{ 54????int?enable?=?pin->mask?&?jack->status; 55?? 56????if?(pin->invert) 57?????enable?=?!enable; 58?? 59????if?(enable) 60?????snd_soc_dapm_enable_pin(dapm,?pin->pin); 61????else 62?????snd_soc_dapm_disable_pin(dapm,?pin->pin); 63?? 64????/*?we?need?to?sync?for?this?case?only?*/ 65????sync?=?1; 66???} 67?? 68???/*?Report?before?the?DAPM?sync?to?help?users?updating?micbias?status?*/ 69???blocking_notifier_call_chain(&jack->notifier,?jack->status,?jack); 70?? 71???if?(sync) 72????snd_soc_dapm_sync(dapm); 73?? 74???snd_jack_report(jack->jack,?jack->status); 75?? 76???mutex_unlock(&jack->mutex); 77??} 78??EXPORT_SYMBOL_GPL(snd_soc_jack_report); 652??void?snd_jack_report(struct?snd_jack?*jack,?int?status) 653??{ 654???struct?snd_jack_kctl?*jack_kctl; 655???unsigned?int?mask_bits?=?0; 656??#ifdef?CONFIG_SND_JACK_input_DEV 657???int?i; 658??#endif 659?? 660???if?(!jack) 661????return; 662?? 663???jack->hw_status_cache?=?status; 664?? 665???list_for_each_entry(jack_kctl,?&jack->kctl_list,?list) 666????if?(jack_kctl->sw_inject_enable) 667?????mask_bits?|=?jack_kctl->mask_bits; 668????else 669?????snd_kctl_jack_report(jack->card,?jack_kctl->kctl, 670????????????status?&?jack_kctl->mask_bits); 671?? 672??#ifdef?CONFIG_SND_JACK_INPUT_DEV 673???mutex_lock(&jack->input_dev_lock); 674???if?(!jack->input_dev)?{ 675????mutex_unlock(&jack->input_dev_lock); 676????return; 677???} 678?? 679???for?(i?=?0;?i?key);?i++)?{ 680????int?testbit?=?((SND_JACK_BTN_0?>>?i)?&?~mask_bits); 681?? 682????if?(jack->type?&?testbit) 683?????input_report_key(jack->input_dev,?jack->key[i], 684????????status?&?testbit); 685???} 686?? 687???for?(i?=?0;?i?if?(jack->type?&?testbit) 691?????input_report_switch(jack->input_dev, 692???????????jack_switch_types[i], 693???????????status?&?testbit); 694???} 695?? 696???input_sync(jack->input_dev); 697???mutex_unlock(&jack->input_dev_lock); 698??#endif?/*?CONFIG_SND_JACK_INPUT_DEV?*/ 699??} 700??EXPORT_SYMBOL(snd_jack_report);
第 683 行,input 上報事件,參數(shù) 1 為耳機事件類型,參數(shù) 2 為耳機事件鍵值,參數(shù) 3 表示耳機插撥的狀態(tài)。第 696 行,同步事件。
若你要使用 Linux 自帶的耳機拔插檢測驅動,則需要在對應的聲卡驅動的 dts 節(jié)點中聲明你所使用的 GPIO 口,加載時就會自動幫你配置好檢測邏輯。
Linux 自帶的耳機拔插檢測功能有限,大部分平臺都有自己的耳機檢測邏輯,例如 RK 平臺的耳機檢測在這:
kernel/drivers/headset_observe/rockchip_headset_core.c
MTK 平臺的耳機拔插檢測驅動在:
kernel/drivers/misc/mediatek/accdet/
kernel/sound/soc/codecs/mt6xxx-accdet.c