diff options
-rw-r--r-- | include/sound/soc-dapm.h | 43 | ||||
-rw-r--r-- | include/sound/soc.h | 4 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic3x.c | 2 | ||||
-rw-r--r-- | sound/soc/codecs/wm8960.c | 4 | ||||
-rw-r--r-- | sound/soc/sh/fsi.c | 80 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 6 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 364 |
7 files changed, 315 insertions, 188 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 2f76a51bbaac..8031769ac485 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -93,6 +93,9 @@ #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} +#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ +{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ @@ -148,6 +151,11 @@ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} +#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ + wevent, wflags) \ +{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ + .event = wevent, .event_flags = wflags} /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ @@ -227,13 +235,6 @@ .info = snd_soc_info_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } -#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, max, invert, \ - power) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .info = snd_soc_info_volsw, \ - .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\ - ((max) << 16) | ((invert) << 24) } #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ @@ -241,15 +242,6 @@ .tlv.p = (tlv_array), \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } -#define SOC_DAPM_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, \ - power, tlv_array) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ - .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw, \ - .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\ - ((max) << 16) | ((invert) << 24) } #define SOC_DAPM_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ @@ -373,6 +365,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ snd_soc_dapm_output, /* output pin */ snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ + snd_soc_dapm_virt_mux, /* virtual version of snd_soc_dapm_mux */ snd_soc_dapm_value_mux, /* selects 1 analog signal from many inputs */ snd_soc_dapm_mixer, /* mixes several analog signals together */ snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */ @@ -476,18 +469,32 @@ struct snd_soc_dapm_widget { struct list_head power_list; }; +struct snd_soc_dapm_update { + struct snd_soc_dapm_widget *widget; + struct snd_kcontrol *kcontrol; + int reg; + int mask; + int val; +}; + /* DAPM context */ struct snd_soc_dapm_context { - struct list_head widgets; - struct list_head paths; + int n_widgets; /* number of widgets in this context */ enum snd_soc_bias_level bias_level; enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ + struct snd_soc_dapm_update *update; + struct device *dev; /* from parent - for debug */ struct snd_soc_codec *codec; /* parent codec */ struct snd_soc_card *card; /* parent card */ + + /* used during DAPM updates */ + int dev_power; + struct list_head list; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dapm; #endif diff --git a/include/sound/soc.h b/include/sound/soc.h index 7e65b015f8dd..74921f20a1d8 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -661,6 +661,10 @@ struct snd_soc_card { struct list_head platform_dev_list; struct list_head dai_dev_list; + struct list_head widgets; + struct list_head paths; + struct list_head dapm_list; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; struct dentry *debugfs_pop_time; diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 8cd4cf5c44dc..3bedab26892f 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -185,7 +185,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->paths, list) { + list_for_each_entry(path, &widget->dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 2c5712dce1d5..054f5737319c 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -418,7 +418,9 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) * list each time to find the desired power state do so now * and save the result. */ - list_for_each_entry(w, &codec->dapm.widgets, list) { + list_for_each_entry(w, &codec->card->widgets, list) { + if (w->dapm != &codec->dapm) + continue; if (strcmp(w->name, "LOUT1 PGA") == 0) wm8960->lout1 = w; if (strcmp(w->name, "ROUT1 PGA") == 0) diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 97c5394aa7d7..2b06402801ef 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -134,6 +134,9 @@ struct fsi_stream { int buff_len; int period_len; int period_num; + + int uerr_num; + int oerr_num; }; struct fsi_priv { @@ -326,17 +329,29 @@ static void fsi_stream_push(struct fsi_priv *fsi, io->buff_offset = 0; io->period_len = period_len; io->period_num = 0; + io->oerr_num = -1; /* ignore 1st err */ + io->uerr_num = -1; /* ignore 1st err */ } static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) { struct fsi_stream *io = fsi_get_stream(fsi, is_play); + struct snd_soc_dai *dai = fsi_get_dai(io->substream); + + + if (io->oerr_num > 0) + dev_err(dai->dev, "over_run = %d\n", io->oerr_num); + + if (io->uerr_num > 0) + dev_err(dai->dev, "under_run = %d\n", io->uerr_num); io->substream = NULL; io->buff_len = 0; io->buff_offset = 0; io->period_len = 0; io->period_num = 0; + io->oerr_num = 0; + io->uerr_num = 0; } static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) @@ -375,6 +390,27 @@ static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play) return frames_to_bytes(runtime, 1) / io->chan_num; } +static void fsi_count_fifo_err(struct fsi_priv *fsi) +{ + u32 ostatus = fsi_reg_read(fsi, DOFF_ST); + u32 istatus = fsi_reg_read(fsi, DIFF_ST); + + if (ostatus & ERR_OVER) + fsi->playback.oerr_num++; + + if (ostatus & ERR_UNDER) + fsi->playback.uerr_num++; + + if (istatus & ERR_OVER) + fsi->capture.oerr_num++; + + if (istatus & ERR_UNDER) + fsi->capture.uerr_num++; + + fsi_reg_write(fsi, DOFF_ST, 0); + fsi_reg_write(fsi, DIFF_ST, 0); +} + /* * dma function */ @@ -574,7 +610,7 @@ static void fsi_soft_all_reset(struct fsi_master *master) mdelay(10); } -static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int startup, int stream) +static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) { struct snd_pcm_runtime *runtime; struct snd_pcm_substream *substream = NULL; @@ -667,40 +703,20 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int startup, int stream) /* update buff_offset */ io->buff_offset += fsi_num2offset(data_num, ch_width); - /* check fifo status */ - if (!startup) { - struct snd_soc_dai *dai = fsi_get_dai(substream); - u32 status = is_play ? - fsi_reg_read(fsi, DOFF_ST) : - fsi_reg_read(fsi, DIFF_ST); - - if (status & ERR_OVER) - dev_err(dai->dev, "over run\n"); - if (status & ERR_UNDER) - dev_err(dai->dev, "under run\n"); - } - - is_play ? - fsi_reg_write(fsi, DOFF_ST, 0) : - fsi_reg_write(fsi, DIFF_ST, 0); - - /* re-enable irq */ - fsi_irq_enable(fsi, is_play); - if (over_period) snd_pcm_period_elapsed(substream); return 0; } -static int fsi_data_pop(struct fsi_priv *fsi, int startup) +static int fsi_data_pop(struct fsi_priv *fsi) { - return fsi_fifo_data_ctrl(fsi, startup, SNDRV_PCM_STREAM_CAPTURE); + return fsi_fifo_data_ctrl(fsi, SNDRV_PCM_STREAM_CAPTURE); } -static int fsi_data_push(struct fsi_priv *fsi, int startup) +static int fsi_data_push(struct fsi_priv *fsi) { - return fsi_fifo_data_ctrl(fsi, startup, SNDRV_PCM_STREAM_PLAYBACK); + return fsi_fifo_data_ctrl(fsi, SNDRV_PCM_STREAM_PLAYBACK); } static irqreturn_t fsi_interrupt(int irq, void *data) @@ -713,13 +729,16 @@ static irqreturn_t fsi_interrupt(int irq, void *data) fsi_master_mask_set(master, SOFT_RST, IR, IR); if (int_st & AB_IO(1, AO_SHIFT)) - fsi_data_push(&master->fsia, 0); + fsi_data_push(&master->fsia); if (int_st & AB_IO(1, BO_SHIFT)) - fsi_data_push(&master->fsib, 0); + fsi_data_push(&master->fsib); if (int_st & AB_IO(1, AI_SHIFT)) - fsi_data_pop(&master->fsia, 0); + fsi_data_pop(&master->fsia); if (int_st & AB_IO(1, BI_SHIFT)) - fsi_data_pop(&master->fsib, 0); + fsi_data_pop(&master->fsib); + + fsi_count_fifo_err(&master->fsia); + fsi_count_fifo_err(&master->fsib); fsi_irq_clear_status(&master->fsia); fsi_irq_clear_status(&master->fsib); @@ -858,7 +877,8 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, fsi_stream_push(fsi, is_play, substream, frames_to_bytes(runtime, runtime->buffer_size), frames_to_bytes(runtime, runtime->period_size)); - ret = is_play ? fsi_data_push(fsi, 1) : fsi_data_pop(fsi, 1); + ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); + fsi_irq_enable(fsi, is_play); break; case SNDRV_PCM_TRIGGER_STOP: fsi_irq_disable(fsi, is_play); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1dc4b11c1988..a233607a73c6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1427,6 +1427,7 @@ static int soc_probe_codec(struct snd_soc_card *card, /* mark codec as probed and add to card codec list */ codec->probed = 1; list_add(&codec->card_list, &card->codec_dev_list); + list_add(&codec->dapm.list, &card->dapm_list); return ret; } @@ -1879,6 +1880,9 @@ static int soc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&card->dai_dev_list); INIT_LIST_HEAD(&card->codec_dev_list); INIT_LIST_HEAD(&card->platform_dev_list); + INIT_LIST_HEAD(&card->widgets); + INIT_LIST_HEAD(&card->paths); + INIT_LIST_HEAD(&card->dapm_list); soc_init_card_debugfs(card); @@ -3480,8 +3484,6 @@ int snd_soc_register_codec(struct device *dev, else codec->compress_type = SND_SOC_FLAT_COMPRESSION; - INIT_LIST_HEAD(&codec->dapm.widgets); - INIT_LIST_HEAD(&codec->dapm.paths); codec->write = codec_drv->write; codec->read = codec_drv->read; codec->dapm.bias_level = SND_SOC_BIAS_OFF; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 3d310af28907..50f5c785cb61 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -56,6 +56,7 @@ static int dapm_up_seq[] = { [snd_soc_dapm_aif_out] = 3, [snd_soc_dapm_mic] = 4, [snd_soc_dapm_mux] = 5, + [snd_soc_dapm_virt_mux] = 5, [snd_soc_dapm_value_mux] = 5, [snd_soc_dapm_dac] = 6, [snd_soc_dapm_mixer] = 7, @@ -81,6 +82,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_mic] = 7, [snd_soc_dapm_micbias] = 8, [snd_soc_dapm_mux] = 9, + [snd_soc_dapm_virt_mux] = 9, [snd_soc_dapm_value_mux] = 9, [snd_soc_dapm_aif_in] = 10, [snd_soc_dapm_aif_out] = 10, @@ -216,6 +218,20 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, } } break; + case snd_soc_dapm_virt_mux: { + struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; + + p->connect = 0; + /* since a virtual mux has no backing registers to + * decide which path to connect, it will try to match + * with the first enumeration. This is to ensure + * that the default mux choice (the first) will be + * correctly powered up during initialization. + */ + if (!strcmp(p->name, e->texts[0])) + p->connect = 1; + } + break; case snd_soc_dapm_value_mux: { struct soc_enum *e = (struct soc_enum *) w->kcontrols[i].private_value; @@ -272,7 +288,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, for (i = 0; i < e->max; i++) { if (!(strcmp(control_name, e->texts[i]))) { - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); path->name = (char*)e->texts[i]; @@ -294,7 +310,7 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, /* search for mixer kcontrol */ for (i = 0; i < dest->num_kcontrols; i++) { if (!strcmp(control_name, dest->kcontrols[i].name)) { - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); path->name = dest->kcontrols[i].name; @@ -453,7 +469,7 @@ static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_path *p; - list_for_each_entry(p, &dapm->paths, list) + list_for_each_entry(p, &dapm->card->paths, list) p->walked = 0; } @@ -847,19 +863,22 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, LIST_HEAD(pending); int cur_sort = -1; int cur_reg = SND_SOC_NOPM; + struct snd_soc_dapm_context *cur_dapm = NULL; int ret; list_for_each_entry_safe(w, n, list, power_list) { ret = 0; /* Do we need to apply any queued changes? */ - if (sort[w->id] != cur_sort || w->reg != cur_reg) { + if (sort[w->id] != cur_sort || w->reg != cur_reg || + w->dapm != cur_dapm) { if (!list_empty(&pending)) - dapm_seq_run_coalesced(dapm, &pending); + dapm_seq_run_coalesced(cur_dapm, &pending); INIT_LIST_HEAD(&pending); cur_sort = -1; cur_reg = SND_SOC_NOPM; + cur_dapm = NULL; } switch (w->id) { @@ -903,6 +922,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, /* Queue it up for application */ cur_sort = sort[w->id]; cur_reg = w->reg; + cur_dapm = w->dapm; list_move(&w->power_list, &pending); break; } @@ -916,6 +936,41 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, dapm_seq_run_coalesced(dapm, &pending); } +static void dapm_widget_update(struct snd_soc_dapm_context *dapm) +{ + struct snd_soc_dapm_update *update = dapm->update; + struct snd_soc_dapm_widget *w; + int ret; + + if (!update) + return; + + w = update->widget; + + if (w->event && + (w->event_flags & SND_SOC_DAPM_PRE_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); + if (ret != 0) + pr_err("%s DAPM pre-event failed: %d\n", + w->name, ret); + } + + ret = snd_soc_update_bits(w->codec, update->reg, update->mask, + update->val); + if (ret < 0) + pr_err("%s DAPM update failed: %d\n", w->name, ret); + + if (w->event && + (w->event_flags & SND_SOC_DAPM_POST_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); + if (ret != 0) + pr_err("%s DAPM post-event failed: %d\n", + w->name, ret); + } +} + + + /* * Scan each dapm widget for complete audio path. * A complete path is a route that has valid endpoints i.e.:- @@ -929,18 +984,22 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) { struct snd_soc_card *card = dapm->codec->card; struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_context *d; LIST_HEAD(up_list); LIST_HEAD(down_list); int ret = 0; int power; - int sys_power = 0; trace_snd_soc_dapm_start(card); + list_for_each_entry(d, &card->dapm_list, list) + if (d->n_widgets) + d->dev_power = 0; + /* Check which widgets we need to power and store them in * lists indicating if they should be powered up or down. */ - list_for_each_entry(w, &dapm->widgets, list) { + list_for_each_entry(w, &card->widgets, list) { switch (w->id) { case snd_soc_dapm_pre: dapm_seq_insert(w, &down_list, dapm_down_seq); @@ -958,7 +1017,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) else power = 1; if (power) - sys_power = 1; + w->dapm->dev_power = 1; if (w->power == power) continue; @@ -978,26 +1037,26 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) /* If there are no DAPM widgets then try to figure out power from the * event type. */ - if (list_empty(&dapm->widgets)) { + if (!dapm->n_widgets) { switch (event) { case SND_SOC_DAPM_STREAM_START: case SND_SOC_DAPM_STREAM_RESUME: - sys_power = 1; + dapm->dev_power = 1; break; case SND_SOC_DAPM_STREAM_STOP: - sys_power = !!dapm->codec->active; + dapm->dev_power = !!dapm->codec->active; break; case SND_SOC_DAPM_STREAM_SUSPEND: - sys_power = 0; + dapm->dev_power = 0; break; case SND_SOC_DAPM_STREAM_NOP: switch (dapm->bias_level) { case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_OFF: - sys_power = 0; + dapm->dev_power = 0; break; default: - sys_power = 1; + dapm->dev_power = 1; break; } break; @@ -1006,52 +1065,64 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } } - if (sys_power && dapm->bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_dapm_set_bias_level(card, dapm, - SND_SOC_BIAS_STANDBY); - if (ret != 0) - dev_err(dapm->dev, - "Failed to turn on bias: %d\n", ret); - } + list_for_each_entry(d, &dapm->card->dapm_list, list) { + if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, + "Failed to turn on bias: %d\n", ret); + } - /* If we're changing to all on or all off then prepare */ - if ((sys_power && dapm->bias_level == SND_SOC_BIAS_STANDBY) || - (!sys_power && dapm->bias_level == SND_SOC_BIAS_ON)) { - ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_PREPARE); - if (ret != 0) - dev_err(dapm->dev, - "Failed to prepare bias: %d\n", ret); + /* If we're changing to all on or all off then prepare */ + if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || + (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_PREPARE); + if (ret != 0) + dev_err(d->dev, + "Failed to prepare bias: %d\n", ret); + } } /* Power down widgets first; try to avoid amplifying pops. */ dapm_seq_run(dapm, &down_list, event, dapm_down_seq); + dapm_widget_update(dapm); + /* Now power up. */ dapm_seq_run(dapm, &up_list, event, dapm_up_seq); - /* If we just powered the last thing off drop to standby bias */ - if (dapm->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { - ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_STANDBY); - if (ret != 0) - dev_err(dapm->dev, - "Failed to apply standby bias: %d\n", ret); - } + list_for_each_entry(d, &dapm->card->dapm_list, list) { + /* If we just powered the last thing off drop to standby bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, + "Failed to apply standby bias: %d\n", + ret); + } - /* If we're in standby and can support bias off then do that */ - if (dapm->bias_level == SND_SOC_BIAS_STANDBY && - dapm->idle_bias_off) { - ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_OFF); - if (ret != 0) - dev_err(dapm->dev, - "Failed to turn off bias: %d\n", ret); - } + /* If we're in standby and can support bias off then do that */ + if (d->bias_level == SND_SOC_BIAS_STANDBY && + d->idle_bias_off) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_OFF); + if (ret != 0) + dev_err(d->dev, + "Failed to turn off bias: %d\n", ret); + } - /* If we just powered up then move to active bias */ - if (dapm->bias_level == SND_SOC_BIAS_PREPARE && sys_power) { - ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_ON); - if (ret != 0) - dev_err(dapm->dev, - "Failed to apply active bias: %d\n", ret); + /* If we just powered up then move to active bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_ON); + if (ret != 0) + dev_err(d->dev, + "Failed to apply active bias: %d\n", + ret); + } } pop_dbg(dapm->dev, card->pop_time, @@ -1145,8 +1216,8 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) if (!dapm->debugfs_dapm) return; - list_for_each_entry(w, &dapm->widgets, list) { - if (!w->name) + list_for_each_entry(w, &dapm->card->widgets, list) { + if (!w->name || w->dapm != dapm) continue; d = debugfs_create_file(w->name, 0444, @@ -1173,6 +1244,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, int found = 0; if (widget->id != snd_soc_dapm_mux && + widget->id != snd_soc_dapm_virt_mux && widget->id != snd_soc_dapm_value_mux) return -ENODEV; @@ -1180,7 +1252,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, return 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->paths, list) { + list_for_each_entry(path, &widget->dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -1214,7 +1286,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, return -ENODEV; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->paths, list) { + list_for_each_entry(path, &widget->dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -1241,7 +1313,9 @@ static ssize_t dapm_widget_show(struct device *dev, int count = 0; char *state = "not set"; - list_for_each_entry(w, &codec->dapm.widgets, list) { + list_for_each_entry(w, &codec->card->widgets, list) { + if (w->dapm != &codec->dapm) + continue; /* only display widgets that burnm power */ switch (w->id) { @@ -1303,17 +1377,32 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) struct snd_soc_dapm_widget *w, *next_w; struct snd_soc_dapm_path *p, *next_p; - list_for_each_entry_safe(w, next_w, &dapm->widgets, list) { + list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; list_del(&w->list); + /* + * remove source and sink paths associated to this widget. + * While removing the path, remove reference to it from both + * source and sink widgets so that path is removed only once. + */ + list_for_each_entry_safe(p, next_p, &w->sources, list_sink) { + list_del(&p->list_sink); + list_del(&p->list_source); + list_del(&p->list); + kfree(p->long_name); + kfree(p); + } + list_for_each_entry_safe(p, next_p, &w->sinks, list_source) { + list_del(&p->list_sink); + list_del(&p->list_source); + list_del(&p->list); + kfree(p->long_name); + kfree(p); + } kfree(w->name); kfree(w); } - - list_for_each_entry_safe(p, next_p, &dapm->paths, list) { - list_del(&p->list); - kfree(p->long_name); - kfree(p); - } } static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, @@ -1321,7 +1410,9 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &dapm->widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (!strcmp(w->name, pin)) { dev_dbg(w->dapm->dev, "dapm: pin %s = %d\n", pin, status); @@ -1357,6 +1448,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_path *path; struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; + struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; const char *sink; const char *control = route->control; const char *source; @@ -1376,17 +1468,28 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, source = route->source; } - /* find src and dest widgets */ - list_for_each_entry(w, &dapm->widgets, list) { - + /* + * find src and dest widgets over all widgets but favor a widget from + * current DAPM context + */ + list_for_each_entry(w, &dapm->card->widgets, list) { if (!wsink && !(strcmp(w->name, sink))) { - wsink = w; + wtsink = w; + if (w->dapm == dapm) + wsink = w; continue; } if (!wsource && !(strcmp(w->name, source))) { - wsource = w; + wtsource = w; + if (w->dapm == dapm) + wsource = w; } } + /* use widget from another DAPM context if not found from this */ + if (!wsink) + wsink = wtsink; + if (!wsource) + wsource = wtsource; if (wsource == NULL || wsink == NULL) return -ENODEV; @@ -1420,7 +1523,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, /* connect static paths */ if (control == NULL) { - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; @@ -1442,12 +1545,13 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; return 0; case snd_soc_dapm_mux: + case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: ret = dapm_connect_mux(dapm, wsource, wsink, path, control, &wsink->kcontrols[0]); @@ -1465,7 +1569,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_mic: case snd_soc_dapm_line: case snd_soc_dapm_spk: - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 0; @@ -1524,7 +1628,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &dapm->widgets, list) + list_for_each_entry(w, &dapm->card->widgets, list) { if (w->new) continue; @@ -1537,6 +1641,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) dapm_new_mixer(dapm, w); break; case snd_soc_dapm_mux: + case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: w->power_check = dapm_generic_check_power; dapm_new_mux(dapm, w); @@ -1634,13 +1739,12 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - unsigned int val, val2, val_mask; - int connect; - int ret; + unsigned int val, val_mask; + int connect, change; + struct snd_soc_dapm_update update; val = (ucontrol->value.integer.value[0] & mask); @@ -1648,18 +1752,12 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, val = max - val; val_mask = mask << shift; val = val << shift; - if (shift != rshift) { - val2 = (ucontrol->value.integer.value[1] & mask); - if (invert) - val2 = max - val2; - val_mask |= mask << rshift; - val |= val2 << rshift; - } mutex_lock(&widget->codec->mutex); widget->value = val; - if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { + change = snd_soc_test_bits(widget->codec, reg, val_mask, val); + if (change) { if (val) /* new connection */ connect = invert ? 0:1; @@ -1667,28 +1765,20 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, /* old connection must be powered down */ connect = invert ? 1:0; + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; + dapm_mixer_update_power(widget, kcontrol, connect); + + widget->dapm->update = NULL; } - if (widget->event) { - if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, kcontrol, - SND_SOC_DAPM_PRE_REG); - if (ret < 0) { - ret = 1; - goto out; - } - } - ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); - if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, kcontrol, - SND_SOC_DAPM_POST_REG); - } else - ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); - -out: mutex_unlock(&widget->codec->mutex); - return ret; + return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -1736,7 +1826,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask, bitmask; - int ret = 0; + struct snd_soc_dapm_update update; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; @@ -1755,24 +1845,20 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock(&widget->codec->mutex); widget->value = val; change = snd_soc_test_bits(widget->codec, e->reg, mask, val); - dapm_mux_update_power(widget, kcontrol, change, mux, e); - if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_PRE_REG); - if (ret < 0) - goto out; - } + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + dapm_mux_update_power(widget, kcontrol, change, mux, e); - if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_POST_REG); + widget->dapm->update = NULL; -out: mutex_unlock(&widget->codec->mutex); - return ret; + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -1884,7 +1970,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask; - int ret = 0; + struct snd_soc_dapm_update update; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -1901,24 +1987,20 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mutex_lock(&widget->codec->mutex); widget->value = val; change = snd_soc_test_bits(widget->codec, e->reg, mask, val); - dapm_mux_update_power(widget, kcontrol, change, mux, e); - if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_PRE_REG); - if (ret < 0) - goto out; - } + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + dapm_mux_update_power(widget, kcontrol, change, mux, e); - if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_POST_REG); + widget->dapm->update = NULL; -out: mutex_unlock(&widget->codec->mutex); - return ret; + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); @@ -2024,12 +2106,13 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, else snprintf(w->name, name_len, "%s", widget->name); + dapm->n_widgets++; w->dapm = dapm; w->codec = dapm->codec; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); - list_add(&w->list, &dapm->widgets); + list_add(&w->list, &dapm->card->widgets); /* machine layer set ups unconnected pins and insertions */ w->connected = 1; @@ -2072,9 +2155,9 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &dapm->widgets, list) + list_for_each_entry(w, &dapm->card->widgets, list) { - if (!w->sname) + if (!w->sname || w->dapm != dapm) continue; dev_dbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n", w->name, w->sname, stream, event); @@ -2157,7 +2240,9 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &dapm->widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (!strcmp(w->name, pin)) { dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin); @@ -2222,7 +2307,9 @@ int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &dapm->widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (!strcmp(w->name, pin)) return w->connected; } @@ -2247,7 +2334,9 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &dapm->widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (!strcmp(w->name, pin)) { w->ignore_suspend = 1; return 0; @@ -2269,6 +2358,7 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) { snd_soc_dapm_sys_remove(dapm->dev); dapm_free_widgets(dapm); + list_del(&dapm->list); } EXPORT_SYMBOL_GPL(snd_soc_dapm_free); @@ -2278,7 +2368,9 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) LIST_HEAD(down_list); int powerdown = 0; - list_for_each_entry(w, &dapm->widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (w->power) { dapm_seq_insert(w, &down_list, dapm_down_seq); w->power = 0; |