前面的六篇文章,我們已經討論了dapm關於動態電源管理的有關知識,包括widget的創建和初始化,widget之間的連接以及widget的上下電順序等等。本章我們準備討論dapm框架中的另一個機制:事件機制。通過dapm事件機制,widget可以對它所關心的dapm事件做出反應,這種機制對於擴充widget的能力非常有用,例如,對於那些位於codec之外的widget,好像喇叭功放、外部的前置放大器等等,由於不是使用codec內部的寄存器進行電源控制,我們就必須利用dapm的事件機制,獲得相應的上下電事件,從而可以定製widget自身的電源控制功能。
/*****************************************************************************************************/
聲明:本博內容均由http://blog.csdn.net/droidphone原創,轉載請註明出處,謝謝!
/*****************************************************************************************************/
dapm event的種類
dapm目前爲我們定義了9種dapm event,他們分別是:
事件類型 | 說明 |
---|---|
SND_SOC_DAPM_PRE_PMU | widget要上電前發出的事件 |
SND_SOC_DAPM_POST_PMU | widget要上電後發出的事件 |
SND_SOC_DAPM_PRE_PMD | widget要下電前發出的事件 |
SND_SOC_DAPM_POST_PMD | widget要下電後發出的事件 |
SND_SOC_DAPM_PRE_REG | 音頻路徑設置之前發出的事件 |
SND_SOC_DAPM_POST_REG | 音頻路徑設置之後發出的事件 |
SND_SOC_DAPM_WILL_PMU | 在處理up_list鏈表之前發出的事件 |
SND_SOC_DAPM_WILL_PMD | 在處理down_list鏈表之前發出的事件 |
SND_SOC_DAPM_PRE_POST_PMD | SND_SOC_DAPM_PRE_PMD和 SND_SOC_DAPM_POST_PMD的合併 |
widget的event回調函數
ALSA聲卡驅動中的DAPM詳解之二:widget-具備路徑和電源管理信息的kcontrol中,我們已經介紹過代表widget的snd_soc_widget結構,在這個結構體中,有一個event字段用於保存該widget的事件回調函數,同時,event_flags字段用於保存該widget需要關心的dapm事件種類,只有event_flags字段中相應的事件位被設置了的事件纔會發到event回調函數中進行處理。
我們知道,dapm爲我們提供了常用widget的定義輔助宏,使用以下這幾種輔助宏定義widget時,默認需要我們提供dapm event回調函數
- SND_SOC_DAPM_MIC
- SND_SOC_DAPM_HP
- SND_SOC_DAPM_SPK
- SND_SOC_DAPM_LINE
- /* turn speaker amplifier on/off depending on use */
- static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event)
- {
- gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
- return 0;
- }
- /* corgi machine dapm widgets */
- static const struct snd_soc_dapm_widget wm8731_dapm_widgets =
- SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event);
- SND_SOC_DAPM_PGA_E
- SND_SOC_DAPM_OUT_DRV_E
- SND_SOC_DAPM_MIXER_E
- SND_SOC_DAPM_MIXER_NAMED_CTL_E
- SND_SOC_DAPM_SWITCH_E
- SND_SOC_DAPM_MUX_E
- SND_SOC_DAPM_VIRT_MUX_E
觸發dapm event
我們已經定義好了帶有event回調的widget,那麼,在那裏觸發這些dapm event?答案是:在dapm_power_widgets函數的處理過程中,dapm_power_widgets函數我們已經在ALSA聲卡驅動中的DAPM詳解之六:精髓所在,牽一髮而動全身中做了詳細的分析,其中,在所有需要處理電源變化的widget被分別放入up_list和down_list鏈表後,會相應地發出各種dapm事件:
- static int dapm_power_widgets(struct snd_soc_card *card, int event)
- {
- ......
- list_for_each_entry(w, &down_list, power_list) {
- dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMD);
- }
- list_for_each_entry(w, &up_list, power_list) {
- dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMU);
- }
- /* Power down widgets first; try to avoid amplifying pops. */
- dapm_seq_run(card, &down_list, event, false);
- dapm_widget_update(card);
- /* Now power up. */
- dapm_seq_run(card, &up_list, event, true);
- ......
- }
- static void dapm_seq_run_coalesced(struct snd_soc_card *card,
- struct list_head *pending)
- {
- ......
- list_for_each_entry(w, pending, power_list) {
- ......
- /* Check for events */
- dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMU);
- dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMD);
- }
- if (reg >= 0) {
- ......
- pop_wait(card->pop_time);
- soc_widget_update_bits_locked(w, reg, mask, value);
- }
- list_for_each_entry(w, pending, power_list) {
- dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMU);
- dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMD);
- }
- }
- static void dapm_widget_update(struct snd_soc_card *card)
- {
- struct snd_soc_dapm_update *update = card->update;
- struct snd_soc_dapm_widget_list *wlist;
- struct snd_soc_dapm_widget *w = NULL;
- unsigned int wi;
- int ret;
- if (!update || !dapm_kcontrol_is_powered(update->kcontrol))
- return;
- wlist = dapm_kcontrol_get_wlist(update->kcontrol);
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- w = wlist->widgets[wi];
- if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) {
- ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG);
- ......
- }
- }
- ......
- /* 更新kcontrol的值,改變音頻路徑 */
- ret = soc_widget_update_bits_locked(w, update->reg, update->mask,
- update->val);
- ......
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- w = wlist->widgets[wi];
- if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) {
- ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG);
- ......
- }
- }
- }
dai widget與stream widget
dai widget 在ALSA聲卡驅動中的DAPM詳解之四:在驅動程序中初始化並註冊widget和route一文中,我們已經討論過dai widget,dai widget又分爲cpu dai widget和codec dai widget,它們在machine驅動分別匹配上相應的codec和platform後,由soc_probe_platform和soc_probe_codec這兩個函數通過調用dapm的api函數:
- snd_soc_dapm_new_dai_widgets
- snd_soc_dapm_dai_in 對應playback dai
- snd_soc_dapm_dai_out 對應capture dai
- snd_soc_dapm_aif_in 用SND_SOC_DAPM_AIF_IN輔助宏定義
- snd_soc_dapm_aif_out
用SND_SOC_DAPM_AIF_OUT輔助宏定義
- snd_soc_dapm_dac
用SND_SOC_DAPM_AIF_DAC輔助宏定義
- snd_soc_dapm_adc
用SND_SOC_DAPM_AIF_ADC輔助宏定義
連接dai widget和stream widget
- static int snd_soc_instantiate_card(struct snd_soc_card *card)
- {
- ......
- /* card bind complete so register a sound card */
- ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- card->owner, 0, &card->snd_card);
- ......
- if (card->dapm_widgets)
- snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
- card->num_dapm_widgets);
- /* 建立dai widget和stream widget之間的連接關係 */
- snd_soc_dapm_link_dai_widgets(card);
- ......
- if (card->controls)
- snd_soc_add_card_controls(card, card->controls, card->num_controls);
- ......
- if (card->dapm_routes)
- snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
- card->num_dapm_routes);
- ......
- if (card->fully_routed)
- list_for_each_entry(codec, &card->codec_dev_list, card_list)
- snd_soc_dapm_auto_nc_codec_pins(codec);
- snd_soc_dapm_new_widgets(card);
- ret = snd_card_register(card->snd_card);
- ......
- return 0;
- }
- int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
- {
- struct snd_soc_dapm_widget *dai_w, *w;
- struct snd_soc_dai *dai;
- /* For each DAI widget... */
- list_for_each_entry(dai_w, &card->widgets, list) {
- switch (dai_w->id) {
- case snd_soc_dapm_dai_in:
- case snd_soc_dapm_dai_out:
- break;
- default:
- continue;
- }
- dai = dai_w->priv;
- /* ...find all widgets with the same stream and link them */
- list_for_each_entry(w, &card->widgets, list) {
- if (w->dapm != dai_w->dapm)
- continue;
- switch (w->id) {
- case snd_soc_dapm_dai_in:
- case snd_soc_dapm_dai_out:
- continue;
- default:
- break;
- }
- if (!w->sname || !strstr(w->sname, dai_w->name))
- continue;
- if (dai->driver->playback.stream_name &&
- strstr(w->sname,
- dai->driver->playback.stream_name)) {
- dev_dbg(dai->dev, "%s -> %s\n",
- dai->playback_widget->name, w->name);
- snd_soc_dapm_add_path(w->dapm,
- dai->playback_widget, w, NULL, NULL);
- }
- if (dai->driver->capture.stream_name &&
- strstr(w->sname,
- dai->driver->capture.stream_name)) {
- dev_dbg(dai->dev, "%s -> %s\n",
- w->name, dai->capture_widget->name);
- snd_soc_dapm_add_path(w->dapm, w,
- dai->capture_widget, NULL, NULL);
- }
- }
- }
- return 0;
- SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
- static struct snd_soc_dai_driver wm8993_dai = {
- .name = "wm8993-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = WM8993_RATES,
- .formats = WM8993_FORMATS,
- .sig_bits = 24,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = WM8993_RATES,
- .formats = WM8993_FORMATS,
- .sig_bits = 24,
- },
- .ops = &wm8993_ops,
- .symmetric_rates = 1,
- };
- SND_SOC_DAPM_ADC("ADCL", NULL, WM8993_POWER_MANAGEMENT_2, 1, 0),
- SND_SOC_DAPM_ADC("ADCR", NULL, WM8993_POWER_MANAGEMENT_2, 0, 0),
- SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0),
- SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0),
stream event
把dai widget和stream widget連接在一起,就是爲了能把ASoc中的pcm處理部分和dapm進行關聯,pcm的處理過程中,會通過發出stream event來通知dapm系統,重新掃描並調整音頻路徑上各個widget的電源狀態,目前dapm提供了以下幾種stream event:
- /* dapm stream operations */
- #define SND_SOC_DAPM_STREAM_NOP 0x0
- #define SND_SOC_DAPM_STREAM_START 0x1
- #define SND_SOC_DAPM_STREAM_STOP 0x2
- #define SND_SOC_DAPM_STREAM_SUSPEND 0x4
- #define SND_SOC_DAPM_STREAM_RESUME 0x8
- #define SND_SOC_DAPM_STREAM_PAUSE_PUSH 0x10
- #define SND_SOC_DAPM_STREAM_PAUSE_RELEASE 0x20
- snd_soc_dapm_stream_event(rtd, substream->stream,
- SND_SOC_DAPM_STREAM_START);
- snd_soc_dapm_stream_event(rtd,
- SNDRV_PCM_STREAM_PLAYBACK,
- SND_SOC_DAPM_STREAM_STOP);
- static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
- int event)
- {
- struct snd_soc_dapm_widget *w_cpu, *w_codec;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- w_cpu = cpu_dai->playback_widget;
- w_codec = codec_dai->playback_widget;
- } else {
- w_cpu = cpu_dai->capture_widget;
- w_codec = codec_dai->capture_widget;
- }
- if (w_cpu) {
- dapm_mark_dirty(w_cpu, "stream event");
- switch (event) {
- case SND_SOC_DAPM_STREAM_START:
- w_cpu->active = 1;
- break;
- case SND_SOC_DAPM_STREAM_STOP:
- w_cpu->active = 0;
- break;
- case SND_SOC_DAPM_STREAM_SUSPEND:
- case SND_SOC_DAPM_STREAM_RESUME:
- case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
- case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
- break;
- }
- }
- if (w_codec) {
- dapm_mark_dirty(w_codec, "stream event");
- switch (event) {
- case SND_SOC_DAPM_STREAM_START:
- w_codec->active = 1;
- break;
- case SND_SOC_DAPM_STREAM_STOP:
- w_codec->active = 0;
- break;
- case SND_SOC_DAPM_STREAM_SUSPEND:
- case SND_SOC_DAPM_STREAM_RESUME:
- case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
- case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
- break;
- }
- }
- dapm_power_widgets(rtd->card, event);