]> Pileus Git - ~andy/linux/blobdiff - sound/soc/soc-dapm.c
ASoC: sh: fsi: enable chip specific data transfer mode
[~andy/linux] / sound / soc / soc-dapm.c
index d7ee73a60ca556a1d41ee9c576d1ca0ae50cb127..90ee77d2409da8402ea58026b788a624f850a25a 100644 (file)
@@ -68,6 +68,7 @@ static int dapm_up_seq[] = {
        [snd_soc_dapm_out_drv] = 10,
        [snd_soc_dapm_hp] = 10,
        [snd_soc_dapm_spk] = 10,
+       [snd_soc_dapm_line] = 10,
        [snd_soc_dapm_post] = 11,
 };
 
@@ -76,6 +77,7 @@ static int dapm_down_seq[] = {
        [snd_soc_dapm_adc] = 1,
        [snd_soc_dapm_hp] = 2,
        [snd_soc_dapm_spk] = 2,
+       [snd_soc_dapm_line] = 2,
        [snd_soc_dapm_out_drv] = 2,
        [snd_soc_dapm_pga] = 4,
        [snd_soc_dapm_mixer_named_ctl] = 5,
@@ -705,11 +707,51 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
        }
 }
 
+/* add widget to list if it's not already in the list */
+static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list,
+       struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_widget_list *wlist;
+       int wlistsize, wlistentries, i;
+
+       if (*list == NULL)
+               return -EINVAL;
+
+       wlist = *list;
+
+       /* is this widget already in the list */
+       for (i = 0; i < wlist->num_widgets; i++) {
+               if (wlist->widgets[i] == w)
+                       return 0;
+       }
+
+       /* allocate some new space */
+       wlistentries = wlist->num_widgets + 1;
+       wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+                       wlistentries * sizeof(struct snd_soc_dapm_widget *);
+       *list = krealloc(wlist, wlistsize, GFP_KERNEL);
+       if (*list == NULL) {
+               dev_err(w->dapm->dev, "can't allocate widget list for %s\n",
+                       w->name);
+               return -ENOMEM;
+       }
+       wlist = *list;
+
+       /* insert the widget */
+       dev_dbg(w->dapm->dev, "added %s in widget list pos %d\n",
+                       w->name, wlist->num_widgets);
+
+       wlist->widgets[wlist->num_widgets] = w;
+       wlist->num_widgets++;
+       return 1;
+}
+
 /*
  * Recursively check for a completed path to an active or physically connected
  * output widget. Returns number of complete paths.
  */
-static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
+static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
+       struct snd_soc_dapm_widget_list **list)
 {
        struct snd_soc_dapm_path *path;
        int con = 0;
@@ -765,9 +807,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
                if (path->walked)
                        continue;
 
+               trace_snd_soc_dapm_output_path(widget, path);
+
                if (path->sink && path->connect) {
                        path->walked = 1;
-                       con += is_connected_output_ep(path->sink);
+
+                       /* do we need to add this widget to the list ? */
+                       if (list) {
+                               int err;
+                               err = dapm_list_add_widget(list, path->sink);
+                               if (err < 0) {
+                                       dev_err(widget->dapm->dev, "could not add widget %s\n",
+                                               widget->name);
+                                       return con;
+                               }
+                       }
+
+                       con += is_connected_output_ep(path->sink, list);
                }
        }
 
@@ -780,7 +836,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
  * Recursively check for a completed path to an active or physically connected
  * input widget. Returns number of complete paths.
  */
-static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
+static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
+       struct snd_soc_dapm_widget_list **list)
 {
        struct snd_soc_dapm_path *path;
        int con = 0;
@@ -848,9 +905,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
                if (path->walked)
                        continue;
 
+               trace_snd_soc_dapm_input_path(widget, path);
+
                if (path->source && path->connect) {
                        path->walked = 1;
-                       con += is_connected_input_ep(path->source);
+
+                       /* do we need to add this widget to the list ? */
+                       if (list) {
+                               int err;
+                               err = dapm_list_add_widget(list, path->sink);
+                               if (err < 0) {
+                                       dev_err(widget->dapm->dev, "could not add widget %s\n",
+                                               widget->name);
+                                       return con;
+                               }
+                       }
+
+                       con += is_connected_input_ep(path->source, list);
                }
        }
 
@@ -859,6 +930,39 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
        return con;
 }
 
+/**
+ * snd_soc_dapm_get_connected_widgets - query audio path and it's widgets.
+ * @dai: the soc DAI.
+ * @stream: stream direction.
+ * @list: list of active widgets for this stream.
+ *
+ * Queries DAPM graph as to whether an valid audio stream path exists for
+ * the initial stream specified by name. This takes into account
+ * current mixer and mux kcontrol settings. Creates list of valid widgets.
+ *
+ * Returns the number of valid paths or negative error.
+ */
+int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
+       struct snd_soc_dapm_widget_list **list)
+{
+       struct snd_soc_card *card = dai->card;
+       int paths;
+
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       dapm_reset(card);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               paths = is_connected_output_ep(dai->playback_widget, list);
+       else
+               paths = is_connected_input_ep(dai->playback_widget, list);
+
+       trace_snd_soc_dapm_connected(paths, stream);
+       dapm_clear_walk(&card->dapm);
+       mutex_unlock(&card->dapm_mutex);
+
+       return paths;
+}
+
 /*
  * Handler for generic register modifier widget.
  */
@@ -915,9 +1019,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
 
        DAPM_UPDATE_STAT(w, power_checks);
 
-       in = is_connected_input_ep(w);
+       in = is_connected_input_ep(w, NULL);
        dapm_clear_walk(w->dapm);
-       out = is_connected_output_ep(w);
+       out = is_connected_output_ep(w, NULL);
        dapm_clear_walk(w->dapm);
        return out != 0 && in != 0;
 }
@@ -940,7 +1044,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
        DAPM_UPDATE_STAT(w, power_checks);
 
        if (w->active) {
-               in = is_connected_input_ep(w);
+               in = is_connected_input_ep(w, NULL);
                dapm_clear_walk(w->dapm);
                return in != 0;
        } else {
@@ -956,7 +1060,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
        DAPM_UPDATE_STAT(w, power_checks);
 
        if (w->active) {
-               out = is_connected_output_ep(w);
+               out = is_connected_output_ep(w, NULL);
                dapm_clear_walk(w->dapm);
                return out != 0;
        } else {
@@ -1558,9 +1662,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
        if (!buf)
                return -ENOMEM;
 
-       in = is_connected_input_ep(w);
+       in = is_connected_input_ep(w, NULL);
        dapm_clear_walk(w->dapm);
-       out = is_connected_output_ep(w);
+       out = is_connected_output_ep(w, NULL);
        dapm_clear_walk(w->dapm);
 
        ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",
@@ -1744,7 +1848,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
                dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
        }
 
-       return 0;
+       return found;
 }
 
 int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
@@ -1756,6 +1860,8 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
        ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e);
        mutex_unlock(&card->dapm_mutex);
+       if (ret > 0)
+               soc_dpcm_runtime_update(widget);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
@@ -1788,7 +1894,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
                dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
        }
 
-       return 0;
+       return found;
 }
 
 int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
@@ -1800,6 +1906,8 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
        ret = soc_dapm_mixer_update_power(widget, kcontrol, connect);
        mutex_unlock(&card->dapm_mutex);
+       if (ret > 0)
+               soc_dpcm_runtime_update(widget);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
@@ -2883,7 +2991,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
        struct snd_soc_dai *source, *sink;
        const struct snd_soc_pcm_stream *config = w->params;
        struct snd_pcm_substream substream;
-       struct snd_pcm_hw_params params;
+       struct snd_pcm_hw_params *params = NULL;
        u64 fmt;
        int ret;
 
@@ -2913,17 +3021,21 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
        }
 
        /* Currently very limited parameter selection */
-       memset(&params, 0, sizeof(params));
-       snd_mask_set(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);
+       params = kzalloc(sizeof(*params), GFP_KERNEL);
+       if (!params) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);
 
-       hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->min =
+       hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min =
                config->rate_min;
-       hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->max =
+       hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max =
                config->rate_max;
 
-       hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->min
+       hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min
                = config->channels_min;
-       hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->max
+       hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max
                = config->channels_max;
 
        memset(&substream, 0, sizeof(substream));
@@ -2933,22 +3045,22 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
                if (source->driver->ops && source->driver->ops->hw_params) {
                        substream.stream = SNDRV_PCM_STREAM_CAPTURE;
                        ret = source->driver->ops->hw_params(&substream,
-                                                            &params, source);
+                                                            params, source);
                        if (ret != 0) {
                                dev_err(source->dev,
                                        "hw_params() failed: %d\n", ret);
-                               return ret;
+                               goto out;
                        }
                }
 
                if (sink->driver->ops && sink->driver->ops->hw_params) {
                        substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
-                       ret = sink->driver->ops->hw_params(&substream, &params,
+                       ret = sink->driver->ops->hw_params(&substream, params,
                                                           sink);
                        if (ret != 0) {
                                dev_err(sink->dev,
                                        "hw_params() failed: %d\n", ret);
-                               return ret;
+                               goto out;
                        }
                }
                break;
@@ -2957,12 +3069,14 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
                ret = snd_soc_dai_digital_mute(sink, 0);
                if (ret != 0 && ret != -ENOTSUPP)
                        dev_warn(sink->dev, "Failed to unmute: %d\n", ret);
+               ret = 0;
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
                ret = snd_soc_dai_digital_mute(sink, 1);
                if (ret != 0 && ret != -ENOTSUPP)
                        dev_warn(sink->dev, "Failed to mute: %d\n", ret);
+               ret = 0;
                break;
 
        default:
@@ -2970,7 +3084,9 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
                return -EINVAL;
        }
 
-       return 0;
+out:
+       kfree(params);
+       return ret;
 }
 
 int snd_soc_dapm_new_pcm(struct snd_soc_card *card,