📄 soc-dapm.c
字号:
path->walked = 1; con += is_connected_input_ep(path->source); } } return con;}/* * Scan each dapm widget for complete audio path. * A complete path is a route that has valid endpoints i.e.:- * * o DAC to output pin. * o Input Pin to ADC. * o Input pin to Output pin (bypass, sidetone) * o DAC to ADC (loopback). */static int dapm_power_widgets(struct snd_soc_codec *codec, int event){ struct snd_soc_dapm_widget *w; int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power; /* do we have a sequenced stream event */ if (event == SND_SOC_DAPM_STREAM_START) { c = ARRAY_SIZE(dapm_up_seq); seq = dapm_up_seq; } else if (event == SND_SOC_DAPM_STREAM_STOP) { c = ARRAY_SIZE(dapm_down_seq); seq = dapm_down_seq; } for(i = 0; i < c; i++) { list_for_each_entry(w, &codec->dapm_widgets, list) { /* is widget in stream order */ if (seq && seq[i] && w->id != seq[i]) continue; /* vmid - no action */ if (w->id == snd_soc_dapm_vmid) continue; /* active ADC */ if (w->id == snd_soc_dapm_adc && w->active) { in = is_connected_input_ep(w); dapm_clear_walk(w->codec); w->power = (in != 0) ? 1 : 0; dapm_update_bits(w); continue; } /* active DAC */ if (w->id == snd_soc_dapm_dac && w->active) { out = is_connected_output_ep(w); dapm_clear_walk(w->codec); w->power = (out != 0) ? 1 : 0; dapm_update_bits(w); continue; } /* programmable gain/attenuation */ if (w->id == snd_soc_dapm_pga) { int on; in = is_connected_input_ep(w); dapm_clear_walk(w->codec); out = is_connected_output_ep(w); dapm_clear_walk(w->codec); w->power = on = (out != 0 && in != 0) ? 1 : 0; if (!on) dapm_set_pga(w, on); /* lower volume to reduce pops */ dapm_update_bits(w); if (on) dapm_set_pga(w, on); /* restore volume from zero */ continue; } /* pre and post event widgets */ if (w->id == snd_soc_dapm_pre) { if (!w->event) continue; if (event == SND_SOC_DAPM_STREAM_START) { ret = w->event(w, SND_SOC_DAPM_PRE_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { ret = w->event(w, SND_SOC_DAPM_PRE_PMD); if (ret < 0) return ret; } continue; } if (w->id == snd_soc_dapm_post) { if (!w->event) continue; if (event == SND_SOC_DAPM_STREAM_START) { ret = w->event(w, SND_SOC_DAPM_POST_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { ret = w->event(w, SND_SOC_DAPM_POST_PMD); if (ret < 0) return ret; } continue; } /* all other widgets */ in = is_connected_input_ep(w); dapm_clear_walk(w->codec); out = is_connected_output_ep(w); dapm_clear_walk(w->codec); power = (out != 0 && in != 0) ? 1 : 0; power_change = (w->power == power) ? 0: 1; w->power = power; /* call any power change event handlers */ if (power_change) { if (w->event) { dbg("power %s event for %s flags %x\n", w->power ? "on" : "off", w->name, w->event_flags); if (power) { /* power up event */ if (w->event_flags & SND_SOC_DAPM_PRE_PMU) { ret = w->event(w, SND_SOC_DAPM_PRE_PMU); if (ret < 0) return ret; } dapm_update_bits(w); if (w->event_flags & SND_SOC_DAPM_POST_PMU){ ret = w->event(w, SND_SOC_DAPM_POST_PMU); if (ret < 0) return ret; } } else { /* power down event */ if (w->event_flags & SND_SOC_DAPM_PRE_PMD) { ret = w->event(w, SND_SOC_DAPM_PRE_PMD); if (ret < 0) return ret; } dapm_update_bits(w); if (w->event_flags & SND_SOC_DAPM_POST_PMD) { ret = w->event(w, SND_SOC_DAPM_POST_PMD); if (ret < 0) return ret; } } } else /* no event handler */ dapm_update_bits(w); } } } return ret;}#if DAPM_DEBUGstatic void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action){ struct snd_soc_dapm_widget *w; struct snd_soc_dapm_path *p = NULL; int in, out; printk("DAPM %s %s\n", codec->name, action); list_for_each_entry(w, &codec->dapm_widgets, list) { /* only display widgets that effect routing */ switch (w->id) { case snd_soc_dapm_pre: case snd_soc_dapm_post: case snd_soc_dapm_vmid: continue; case snd_soc_dapm_mux: case snd_soc_dapm_output: case snd_soc_dapm_input: case snd_soc_dapm_switch: case snd_soc_dapm_hp: case snd_soc_dapm_mic: case snd_soc_dapm_spk: case snd_soc_dapm_line: case snd_soc_dapm_micbias: case snd_soc_dapm_dac: case snd_soc_dapm_adc: case snd_soc_dapm_pga: case snd_soc_dapm_mixer: if (w->name) { in = is_connected_input_ep(w); dapm_clear_walk(w->codec); out = is_connected_output_ep(w); dapm_clear_walk(w->codec); printk("%s: %s in %d out %d\n", w->name, w->power ? "On":"Off",in, out); list_for_each_entry(p, &w->sources, list_sink) { if (p->connect) printk(" in %s %s\n", p->name ? p->name : "static", p->source->name); } list_for_each_entry(p, &w->sinks, list_source) { if (p->connect) printk(" out %s %s\n", p->name ? p->name : "static", p->sink->name); } } break; } }}#endif/* test and update the power status of a mux widget */static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, int mask, int val, struct soc_enum* e){ struct snd_soc_dapm_path *path; int found = 0; if (widget->id != snd_soc_dapm_mux) return -ENODEV; if (!snd_soc_test_bits(widget->codec, e->reg, mask, val)) return 0; /* find dapm widget path assoc with kcontrol */ list_for_each_entry(path, &widget->codec->dapm_paths, list) { if (path->kcontrol != kcontrol) continue; if (!path->name || ! e->texts[val]) continue; found = 1; /* we now need to match the string in the enum to the path */ if (!(strcmp(path->name, e->texts[val]))) path->connect = 1; /* new connection */ else path->connect = 0; /* old connection must be powered down */ } if (found) dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); return 0;}/* test and update the power status of a mixer widget */static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, int reg, int val_mask, int val, int invert){ struct snd_soc_dapm_path *path; int found = 0; if (widget->id != snd_soc_dapm_mixer) return -ENODEV; if (!snd_soc_test_bits(widget->codec, reg, val_mask, val)) return 0; /* find dapm widget path assoc with kcontrol */ list_for_each_entry(path, &widget->codec->dapm_paths, list) { if (path->kcontrol != kcontrol) continue; /* found, now check type */ found = 1; if (val) /* new connection */ path->connect = invert ? 0:1; else /* old connection must be powered down */ path->connect = invert ? 1:0; break; } if (found) dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); return 0;}/* show dapm widget status in sys fs */static ssize_t dapm_widget_show(struct device *dev, struct device_attribute *attr, char *buf){ struct snd_soc_device *devdata = dev_get_drvdata(dev); struct snd_soc_codec *codec = devdata->codec; struct snd_soc_dapm_widget *w; int count = 0; char *state = "not set"; list_for_each_entry(w, &codec->dapm_widgets, list) { /* only display widgets that burnm power */ switch (w->id) { case snd_soc_dapm_hp: case snd_soc_dapm_mic: case snd_soc_dapm_spk: case snd_soc_dapm_line: case snd_soc_dapm_micbias: case snd_soc_dapm_dac: case snd_soc_dapm_adc: case snd_soc_dapm_pga: case snd_soc_dapm_mixer: if (w->name) count += sprintf(buf + count, "%s: %s\n", w->name, w->power ? "On":"Off"); break; default: break; } } switch(codec->dapm_state){ case SNDRV_CTL_POWER_D0: state = "D0"; break; case SNDRV_CTL_POWER_D1: state = "D1"; break; case SNDRV_CTL_POWER_D2: state = "D2"; break; case SNDRV_CTL_POWER_D3hot: state = "D3hot"; break; case SNDRV_CTL_POWER_D3cold: state = "D3cold"; break; } count += sprintf(buf + count, "PM State: %s\n", state); return count;}static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);int snd_soc_dapm_sys_add(struct device *dev){ int ret = 0; if (dapm_status) ret = device_create_file(dev, &dev_attr_dapm_widget); return ret;}static void snd_soc_dapm_sys_remove(struct device *dev){ if (dapm_status) device_remove_file(dev, &dev_attr_dapm_widget);}/* free all dapm widgets and resources */static void dapm_free_widgets(struct snd_soc_codec *codec){ struct snd_soc_dapm_widget *w, *next_w; struct snd_soc_dapm_path *p, *next_p; list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) { list_del(&w->list); kfree(w); } list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) { list_del(&p->list); kfree(p->long_name); kfree(p); }}/** * snd_soc_dapm_sync_endpoints - scan and power dapm paths * @codec: audio codec * * Walks all dapm audio paths and powers widgets according to their * stream or path usage. * * Returns 0 for success. */int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec){ return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);}EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);/** * snd_soc_dapm_connect_input - connect dapm widgets * @codec: audio codec * @sink: name of target widget * @control: mixer control name * @source: name of source name * * Connects 2 dapm widgets together via a named audio path. The sink is * the widget receiving the audio signal, whilst the source is the sender * of the audio signal. * * Returns 0 for success else error. */int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink, const char * control, const char *source){ struct snd_soc_dapm_path *path; struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; int ret = 0; /* find src and dest widgets */ list_for_each_entry(w, &codec->dapm_widgets, list) { if (!wsink && !(strcmp(w->name, sink))) { wsink = w; continue; } if (!wsource && !(strcmp(w->name, source))) { wsource = w; } } if (wsource == NULL || wsink == NULL) return -ENODEV; path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); if (!path) return -ENOMEM; path->source = wsource; path->sink = wsink; INIT_LIST_HEAD(&path->list); INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_sink); /* check for external widgets */ if (wsink->id == snd_soc_dapm_input) { if (wsource->id == snd_soc_dapm_micbias || wsource->id == snd_soc_dapm_mic ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -