Commit 4253f3a8 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/dapm' into asoc-next

parents 24ecc23c 6e588a0d
...@@ -397,6 +397,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, ...@@ -397,6 +397,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num); const struct snd_soc_dapm_route *route, int num);
int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num); const struct snd_soc_dapm_route *route, int num);
void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w);
/* dapm events */ /* dapm events */
void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
...@@ -511,9 +512,18 @@ struct snd_soc_dapm_route { ...@@ -511,9 +512,18 @@ struct snd_soc_dapm_route {
struct snd_soc_dapm_path { struct snd_soc_dapm_path {
const char *name; const char *name;
/* source (input) and sink (output) widgets */ /*
struct snd_soc_dapm_widget *source; * source (input) and sink (output) widgets
struct snd_soc_dapm_widget *sink; * The union is for convience, since it is a lot nicer to type
* p->source, rather than p->node[SND_SOC_DAPM_DIR_IN]
*/
union {
struct {
struct snd_soc_dapm_widget *source;
struct snd_soc_dapm_widget *sink;
};
struct snd_soc_dapm_widget *node[2];
};
/* status */ /* status */
u32 connect:1; /* source and sink widgets are connected */ u32 connect:1; /* source and sink widgets are connected */
...@@ -524,8 +534,7 @@ struct snd_soc_dapm_path { ...@@ -524,8 +534,7 @@ struct snd_soc_dapm_path {
int (*connected)(struct snd_soc_dapm_widget *source, int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink); struct snd_soc_dapm_widget *sink);
struct list_head list_source; struct list_head list_node[2];
struct list_head list_sink;
struct list_head list_kcontrol; struct list_head list_kcontrol;
struct list_head list; struct list_head list;
}; };
...@@ -559,8 +568,7 @@ struct snd_soc_dapm_widget { ...@@ -559,8 +568,7 @@ struct snd_soc_dapm_widget {
unsigned char new_power:1; /* power from this run */ unsigned char new_power:1; /* power from this run */
unsigned char power_checked:1; /* power checked this run */ unsigned char power_checked:1; /* power checked this run */
unsigned char is_supply:1; /* Widget is a supply type widget */ unsigned char is_supply:1; /* Widget is a supply type widget */
unsigned char is_sink:1; /* Widget is a sink type widget */ unsigned char is_ep:2; /* Widget is a endpoint type widget */
unsigned char is_source:1; /* Widget is a source type widget */
int subseq; /* sort within widget type */ int subseq; /* sort within widget type */
int (*power_check)(struct snd_soc_dapm_widget *w); int (*power_check)(struct snd_soc_dapm_widget *w);
...@@ -575,16 +583,14 @@ struct snd_soc_dapm_widget { ...@@ -575,16 +583,14 @@ struct snd_soc_dapm_widget {
struct snd_kcontrol **kcontrols; struct snd_kcontrol **kcontrols;
struct snd_soc_dobj dobj; struct snd_soc_dobj dobj;
/* widget input and outputs */ /* widget input and output edges */
struct list_head sources; struct list_head edges[2];
struct list_head sinks;
/* used during DAPM updates */ /* used during DAPM updates */
struct list_head work_list; struct list_head work_list;
struct list_head power_list; struct list_head power_list;
struct list_head dirty; struct list_head dirty;
int inputs; int endpoints[2];
int outputs;
struct clk *clk; struct clk *clk;
}; };
...@@ -672,4 +678,58 @@ static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level( ...@@ -672,4 +678,58 @@ static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level(
return dapm->bias_level; return dapm->bias_level;
} }
enum snd_soc_dapm_direction {
SND_SOC_DAPM_DIR_IN,
SND_SOC_DAPM_DIR_OUT
};
#define SND_SOC_DAPM_DIR_TO_EP(x) BIT(x)
#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN)
#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT)
/**
* snd_soc_dapm_widget_for_each_sink_path - Iterates over all paths in the
* specified direction of a widget
* @w: The widget
* @dir: Whether to iterate over the paths where the specified widget is the
* incoming or outgoing widgets
* @p: The path iterator variable
*/
#define snd_soc_dapm_widget_for_each_path(w, dir, p) \
list_for_each_entry(p, &w->edges[dir], list_node[dir])
/**
* snd_soc_dapm_widget_for_each_sink_path_safe - Iterates over all paths in the
* specified direction of a widget
* @w: The widget
* @dir: Whether to iterate over the paths where the specified widget is the
* incoming or outgoing widgets
* @p: The path iterator variable
* @next_p: Temporary storage for the next path
*
* This function works like snd_soc_dapm_widget_for_each_sink_path, expect that
* it is safe to remove the current path from the list while iterating
*/
#define snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) \
list_for_each_entry_safe(p, next_p, &w->edges[dir], list_node[dir])
/**
* snd_soc_dapm_widget_for_each_sink_path - Iterates over all paths leaving a
* widget
* @w: The widget
* @p: The path iterator variable
*/
#define snd_soc_dapm_widget_for_each_sink_path(w, p) \
snd_soc_dapm_widget_for_each_path(w, SND_SOC_DAPM_DIR_IN, p)
/**
* snd_soc_dapm_widget_for_each_source_path - Iterates over all paths leading to
* a widget
* @w: The widget
* @p: The path iterator variable
*/
#define snd_soc_dapm_widget_for_each_source_path(w, p) \
snd_soc_dapm_widget_for_each_path(w, SND_SOC_DAPM_DIR_OUT, p)
#endif #endif
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#define DAPM_DIRECT "(direct)" #define DAPM_DIRECT "(direct)"
#define DAPM_ARROW(dir) (((dir) == SND_SOC_DAPM_DIR_OUT) ? "->" : "<-")
struct snd_soc_jack; struct snd_soc_jack;
struct snd_soc_codec; struct snd_soc_codec;
...@@ -152,62 +153,38 @@ TRACE_EVENT(snd_soc_dapm_walk_done, ...@@ -152,62 +153,38 @@ TRACE_EVENT(snd_soc_dapm_walk_done,
(int)__entry->path_checks, (int)__entry->neighbour_checks) (int)__entry->path_checks, (int)__entry->neighbour_checks)
); );
TRACE_EVENT(snd_soc_dapm_output_path, TRACE_EVENT(snd_soc_dapm_path,
TP_PROTO(struct snd_soc_dapm_widget *widget, TP_PROTO(struct snd_soc_dapm_widget *widget,
enum snd_soc_dapm_direction dir,
struct snd_soc_dapm_path *path), struct snd_soc_dapm_path *path),
TP_ARGS(widget, path), TP_ARGS(widget, dir, path),
TP_STRUCT__entry( TP_STRUCT__entry(
__string( wname, widget->name ) __string( wname, widget->name )
__string( pname, path->name ? path->name : DAPM_DIRECT) __string( pname, path->name ? path->name : DAPM_DIRECT)
__string( psname, path->sink->name ) __string( pnname, path->node[dir]->name )
__field( int, path_sink ) __field( int, path_node )
__field( int, path_connect ) __field( int, path_connect )
__field( int, path_dir )
), ),
TP_fast_assign( TP_fast_assign(
__assign_str(wname, widget->name); __assign_str(wname, widget->name);
__assign_str(pname, path->name ? path->name : DAPM_DIRECT); __assign_str(pname, path->name ? path->name : DAPM_DIRECT);
__assign_str(psname, path->sink->name); __assign_str(pnname, path->node[dir]->name);
__entry->path_connect = path->connect; __entry->path_connect = path->connect;
__entry->path_sink = (long)path->sink; __entry->path_node = (long)path->node[dir];
__entry->path_dir = dir;
), ),
TP_printk("%c%s -> %s -> %s", TP_printk("%c%s %s %s %s %s",
(int) __entry->path_sink && (int) __entry->path_node &&
(int) __entry->path_connect ? '*' : ' ', (int) __entry->path_connect ? '*' : ' ',
__get_str(wname), __get_str(pname), __get_str(psname)) __get_str(wname), DAPM_ARROW(__entry->path_dir),
); __get_str(pname), DAPM_ARROW(__entry->path_dir),
__get_str(pnname))
TRACE_EVENT(snd_soc_dapm_input_path,
TP_PROTO(struct snd_soc_dapm_widget *widget,
struct snd_soc_dapm_path *path),
TP_ARGS(widget, path),
TP_STRUCT__entry(
__string( wname, widget->name )
__string( pname, path->name ? path->name : DAPM_DIRECT)
__string( psname, path->source->name )
__field( int, path_source )
__field( int, path_connect )
),
TP_fast_assign(
__assign_str(wname, widget->name);
__assign_str(pname, path->name ? path->name : DAPM_DIRECT);
__assign_str(psname, path->source->name);
__entry->path_connect = path->connect;
__entry->path_source = (long)path->source;
),
TP_printk("%c%s <- %s <- %s",
(int) __entry->path_source &&
(int) __entry->path_connect ? '*' : ' ',
__get_str(wname), __get_str(pname), __get_str(psname))
); );
TRACE_EVENT(snd_soc_dapm_connected, TRACE_EVENT(snd_soc_dapm_connected,
......
...@@ -1298,7 +1298,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) ...@@ -1298,7 +1298,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
dev_dbg(dai->dev, "Stream name=%s\n", dev_dbg(dai->dev, "Stream name=%s\n",
dai->playback_widget->name); dai->playback_widget->name);
w = dai->playback_widget; w = dai->playback_widget;
list_for_each_entry(p, &w->sinks, list_source) { snd_soc_dapm_widget_for_each_sink_path(w, p) {
if (p->connected && !p->connected(w, p->sink)) if (p->connected && !p->connected(w, p->sink))
continue; continue;
...@@ -1317,7 +1317,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) ...@@ -1317,7 +1317,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
dev_dbg(dai->dev, "Stream name=%s\n", dev_dbg(dai->dev, "Stream name=%s\n",
dai->capture_widget->name); dai->capture_widget->name);
w = dai->capture_widget; w = dai->capture_widget;
list_for_each_entry(p, &w->sources, list_sink) { snd_soc_dapm_widget_for_each_source_path(w, p) {
if (p->connected && !p->connected(w, p->sink)) if (p->connected && !p->connected(w, p->sink))
continue; continue;
......
...@@ -47,6 +47,13 @@ ...@@ -47,6 +47,13 @@
#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;
#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \
SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN)
#define snd_soc_dapm_for_each_direction(dir) \
for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \
(dir)++)
static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
const char *control, const char *control,
...@@ -167,44 +174,58 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) ...@@ -167,44 +174,58 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
} }
/* /*
* dapm_widget_invalidate_input_paths() - Invalidate the cached number of input * Common implementation for dapm_widget_invalidate_input_paths() and
* paths * dapm_widget_invalidate_output_paths(). The function is inlined since the
* @w: The widget for which to invalidate the cached number of input paths * combined size of the two specialized functions is only marginally larger then
* * the size of the generic function and at the same time the fast path of the
* The function resets the cached number of inputs for the specified widget and * specialized functions is significantly smaller than the generic function.
* all widgets that can be reached via outgoing paths from the widget.
*
* This function must be called if the number of input paths for a widget might
* have changed. E.g. if the source state of a widget changes or a path is added
* or activated with the widget as the sink.
*/ */
static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) static __always_inline void dapm_widget_invalidate_paths(
struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir)
{ {
struct snd_soc_dapm_widget *sink; enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_widget *node;
struct snd_soc_dapm_path *p; struct snd_soc_dapm_path *p;
LIST_HEAD(list); LIST_HEAD(list);
dapm_assert_locked(w->dapm); dapm_assert_locked(w->dapm);
if (w->inputs == -1) if (w->endpoints[dir] == -1)
return; return;
w->inputs = -1;
list_add_tail(&w->work_list, &list); list_add_tail(&w->work_list, &list);
w->endpoints[dir] = -1;
list_for_each_entry(w, &list, work_list) { list_for_each_entry(w, &list, work_list) {
list_for_each_entry(p, &w->sinks, list_source) { snd_soc_dapm_widget_for_each_path(w, dir, p) {
if (p->is_supply || p->weak || !p->connect) if (p->is_supply || p->weak || !p->connect)
continue; continue;
sink = p->sink; node = p->node[rdir];
if (sink->inputs != -1) { if (node->endpoints[dir] != -1) {
sink->inputs = -1; node->endpoints[dir] = -1;
list_add_tail(&sink->work_list, &list); list_add_tail(&node->work_list, &list);
} }
} }
} }
} }
/*
* dapm_widget_invalidate_input_paths() - Invalidate the cached number of
* input paths
* @w: The widget for which to invalidate the cached number of input paths
*
* Resets the cached number of inputs for the specified widget and all widgets
* that can be reached via outcoming paths from the widget.
*
* This function must be called if the number of output paths for a widget might
* have changed. E.g. if the source state of a widget changes or a path is added
* or activated with the widget as the sink.
*/
static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
{
dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_IN);
}
/* /*
* dapm_widget_invalidate_output_paths() - Invalidate the cached number of * dapm_widget_invalidate_output_paths() - Invalidate the cached number of
* output paths * output paths
...@@ -219,29 +240,7 @@ static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) ...@@ -219,29 +240,7 @@ static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
*/ */
static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w) static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)
{ {
struct snd_soc_dapm_widget *source; dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_OUT);
struct snd_soc_dapm_path *p;
LIST_HEAD(list);
dapm_assert_locked(w->dapm);
if (w->outputs == -1)
return;
w->outputs = -1;
list_add_tail(&w->work_list, &list);
list_for_each_entry(w, &list, work_list) {
list_for_each_entry(p, &w->sources, list_sink) {
if (p->is_supply || p->weak || !p->connect)
continue;
source = p->source;
if (source->outputs != -1) {
source->outputs = -1;
list_add_tail(&source->work_list, &list);
}
}
}
} }
/* /*
...@@ -270,9 +269,9 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p) ...@@ -270,9 +269,9 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
* endpoints is either connected or disconnected that sum won't change, * endpoints is either connected or disconnected that sum won't change,
* so there is no need to re-check the path. * so there is no need to re-check the path.
*/ */
if (p->source->inputs != 0) if (p->source->endpoints[SND_SOC_DAPM_DIR_IN] != 0)
dapm_widget_invalidate_input_paths(p->sink); dapm_widget_invalidate_input_paths(p->sink);
if (p->sink->outputs != 0) if (p->sink->endpoints[SND_SOC_DAPM_DIR_OUT] != 0)
dapm_widget_invalidate_output_paths(p->source); dapm_widget_invalidate_output_paths(p->source);
} }
...@@ -283,11 +282,11 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card) ...@@ -283,11 +282,11 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
mutex_lock(&card->dapm_mutex); mutex_lock(&card->dapm_mutex);
list_for_each_entry(w, &card->widgets, list) { list_for_each_entry(w, &card->widgets, list) {
if (w->is_sink || w->is_source) { if (w->is_ep) {
dapm_mark_dirty(w, "Rechecking endpoints"); dapm_mark_dirty(w, "Rechecking endpoints");
if (w->is_sink) if (w->is_ep & SND_SOC_DAPM_EP_SINK)
dapm_widget_invalidate_output_paths(w); dapm_widget_invalidate_output_paths(w);
if (w->is_source) if (w->is_ep & SND_SOC_DAPM_EP_SOURCE)
dapm_widget_invalidate_input_paths(w); dapm_widget_invalidate_input_paths(w);
} }
} }
...@@ -894,7 +893,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) ...@@ -894,7 +893,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
/* add kcontrol */ /* add kcontrol */
for (i = 0; i < w->num_kcontrols; i++) { for (i = 0; i < w->num_kcontrols; i++) {
/* match name */ /* match name */
list_for_each_entry(path, &w->sources, list_sink) { snd_soc_dapm_widget_for_each_source_path(w, path) {
/* mixer/mux paths name must match control name */ /* mixer/mux paths name must match control name */
if (path->name != (char *)w->kcontrol_news[i].name) if (path->name != (char *)w->kcontrol_news[i].name)
continue; continue;
...@@ -923,18 +922,18 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) ...@@ -923,18 +922,18 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
static int dapm_new_mux(struct snd_soc_dapm_widget *w) static int dapm_new_mux(struct snd_soc_dapm_widget *w)
{ {
struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_dapm_context *dapm = w->dapm;
enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_path *path; struct snd_soc_dapm_path *path;
struct list_head *paths;
const char *type; const char *type;
int ret; int ret;
switch (w->id) { switch (w->id) {
case snd_soc_dapm_mux: case snd_soc_dapm_mux:
paths = &w->sources; dir = SND_SOC_DAPM_DIR_OUT;
type = "mux"; type = "mux";
break; break;
case snd_soc_dapm_demux: case snd_soc_dapm_demux:
paths = &w->sinks; dir = SND_SOC_DAPM_DIR_IN;
type = "demux"; type = "demux";
break; break;
default: default:
...@@ -948,7 +947,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) ...@@ -948,7 +947,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
return -EINVAL; return -EINVAL;
} }
if (list_empty(paths)) { if (list_empty(&w->edges[dir])) {
dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name); dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name);
return -EINVAL; return -EINVAL;
} }
...@@ -957,16 +956,9 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) ...@@ -957,16 +956,9 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
if (ret < 0) if (ret < 0)
return ret; return ret;
if (w->id == snd_soc_dapm_mux) { snd_soc_dapm_widget_for_each_path(w, dir, path) {
list_for_each_entry(path, &w->sources, list_sink) { if (path->name)
if (path->name) dapm_kcontrol_add_path(w->kcontrols[0], path);
dapm_kcontrol_add_path(w->kcontrols[0], path);
}
} else {
list_for_each_entry(path, &w->sinks, list_source) {
if (path->name)
dapm_kcontrol_add_path(w->kcontrols[0], path);
}
} }
return 0; return 0;
...@@ -1032,66 +1024,59 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) ...@@ -1032,66 +1024,59 @@ 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_widget_list_create(struct snd_soc_dapm_widget_list **list,
static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list, struct list_head *widgets)
struct snd_soc_dapm_widget *w)
{ {
struct snd_soc_dapm_widget_list *wlist; struct snd_soc_dapm_widget *w;
int wlistsize, wlistentries, i; struct list_head *it;
unsigned int size = 0;
if (*list == NULL) unsigned int i = 0;
return -EINVAL;
wlist = *list;
/* is this widget already in the list */ list_for_each(it, widgets)
for (i = 0; i < wlist->num_widgets; i++) { size++;
if (wlist->widgets[i] == w)
return 0;
}
/* allocate some new space */ *list = kzalloc(sizeof(**list) + size * sizeof(*w), GFP_KERNEL);
wlistentries = wlist->num_widgets + 1; if (*list == NULL)
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, "ASoC: can't allocate widget list for %s\n",
w->name);
return -ENOMEM; return -ENOMEM;
}
wlist = *list;
/* insert the widget */ list_for_each_entry(w, widgets, work_list)
dev_dbg(w->dapm->dev, "ASoC: added %s in widget list pos %d\n", (*list)->widgets[i++] = w;
w->name, wlist->num_widgets);
wlist->widgets[wlist->num_widgets] = w; (*list)->num_widgets = i;
wlist->num_widgets++;
return 1; return 0;
} }
/* /*
* Recursively check for a completed path to an active or physically connected * Common implementation for is_connected_output_ep() and
* output widget. Returns number of complete paths. * is_connected_input_ep(). The function is inlined since the combined size of
* the two specialized functions is only marginally larger then the size of the
* generic function and at the same time the fast path of the specialized
* functions is significantly smaller than the generic function.
*/ */
static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
struct snd_soc_dapm_widget_list **list) struct list_head *list, enum snd_soc_dapm_direction dir,
int (*fn)(struct snd_soc_dapm_widget *, struct list_head *))
{ {
enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_path *path; struct snd_soc_dapm_path *path;
int con = 0; int con = 0;
if (widget->outputs >= 0) if (widget->endpoints[dir] >= 0)
return widget->outputs; return widget->endpoints[dir];
DAPM_UPDATE_STAT(widget, path_checks); DAPM_UPDATE_STAT(widget, path_checks);
if (widget->is_sink && widget->connected) { /* do we need to add this widget to the list ? */
widget->outputs = snd_soc_dapm_suspend_check(widget); if (list)
return widget->outputs; list_add_tail(&widget->work_list, list);
if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {
widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);
return widget->endpoints[dir];
} }
list_for_each_entry(path, &widget->sinks, list_source) { snd_soc_dapm_widget_for_each_path(widget, rdir, path) {
DAPM_UPDATE_STAT(widget, neighbour_checks); DAPM_UPDATE_STAT(widget, neighbour_checks);
if (path->weak || path->is_supply) if (path->weak || path->is_supply)
...@@ -1100,91 +1085,40 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, ...@@ -1100,91 +1085,40 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
if (path->walking) if (path->walking)
return 1; return 1;
trace_snd_soc_dapm_output_path(widget, path); trace_snd_soc_dapm_path(widget, dir, path);
if (path->connect) { if (path->connect) {
path->walking = 1; path->walking = 1;
con += fn(path->node[dir], list);
/* 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,
"ASoC: could not add widget %s\n",
widget->name);
path->walking = 0;
return con;
}
}
con += is_connected_output_ep(path->sink, list);
path->walking = 0; path->walking = 0;
} }
} }
widget->outputs = con; widget->endpoints[dir] = con;
return con; return con;
} }
/*
* 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,
struct list_head *list)
{
return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
is_connected_output_ep);
}
/* /*
* Recursively check for a completed path to an active or physically connected * Recursively check for a completed path to an active or physically connected
* input widget. Returns number of complete paths. * 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 list_head *list)
{ {
struct snd_soc_dapm_path *path; return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
int con = 0; is_connected_input_ep);
if (widget->inputs >= 0)
return widget->inputs;
DAPM_UPDATE_STAT(widget, path_checks);
if (widget->is_source && widget->connected) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->inputs;
}
list_for_each_entry(path, &widget->sources, list_sink) {
DAPM_UPDATE_STAT(widget, neighbour_checks);
if (path->weak || path->is_supply)
continue;
if (path->walking)
return 1;
trace_snd_soc_dapm_input_path(widget, path);
if (path->connect) {
path->walking = 1;
/* do we need to add this widget to the list ? */
if (list) {
int err;
err = dapm_list_add_widget(list, path->source);
if (err < 0) {
dev_err(widget->dapm->dev,
"ASoC: could not add widget %s\n",
widget->name);
path->walking = 0;
return con;
}
}
con += is_connected_input_ep(path->source, list);
path->walking = 0;
}
}
widget->inputs = con;
return con;
} }
/** /**
...@@ -1204,7 +1138,9 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, ...@@ -1204,7 +1138,9 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
{ {
struct snd_soc_card *card = dai->component->card; struct snd_soc_card *card = dai->component->card;
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
LIST_HEAD(widgets);
int paths; int paths;
int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
...@@ -1213,14 +1149,21 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, ...@@ -1213,14 +1149,21 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
* to reset the cached number of inputs and outputs. * to reset the cached number of inputs and outputs.
*/ */
list_for_each_entry(w, &card->widgets, list) { list_for_each_entry(w, &card->widgets, list) {
w->inputs = -1; w->endpoints[SND_SOC_DAPM_DIR_IN] = -1;
w->outputs = -1; w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1;
} }
if (stream == SNDRV_PCM_STREAM_PLAYBACK) if (stream == SNDRV_PCM_STREAM_PLAYBACK)
paths = is_connected_output_ep(dai->playback_widget, list); paths = is_connected_output_ep(dai->playback_widget, &widgets);
else else
paths = is_connected_input_ep(dai->capture_widget, list); paths = is_connected_input_ep(dai->capture_widget, &widgets);
/* Drop starting point */
list_del(widgets.next);
ret = dapm_widget_list_create(list, &widgets);
if (ret)
paths = ret;
trace_snd_soc_dapm_connected(paths, stream); trace_snd_soc_dapm_connected(paths, stream);
mutex_unlock(&card->dapm_mutex); mutex_unlock(&card->dapm_mutex);
...@@ -1321,7 +1264,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) ...@@ -1321,7 +1264,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks); DAPM_UPDATE_STAT(w, power_checks);
/* Check if one of our outputs is connected */ /* Check if one of our outputs is connected */
list_for_each_entry(path, &w->sinks, list_source) { snd_soc_dapm_widget_for_each_sink_path(w, path) {
DAPM_UPDATE_STAT(w, neighbour_checks); DAPM_UPDATE_STAT(w, neighbour_checks);
if (path->weak) if (path->weak)
...@@ -1745,12 +1688,12 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, ...@@ -1745,12 +1688,12 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
/* If we changed our power state perhaps our neigbours changed /* If we changed our power state perhaps our neigbours changed
* also. * also.
*/ */
list_for_each_entry(path, &w->sources, list_sink) snd_soc_dapm_widget_for_each_source_path(w, path)
dapm_widget_set_peer_power(path->source, power, path->connect); dapm_widget_set_peer_power(path->source, power, path->connect);
/* Supplies can't affect their outputs, only their inputs */ /* Supplies can't affect their outputs, only their inputs */
if (!w->is_supply) { if (!w->is_supply) {
list_for_each_entry(path, &w->sinks, list_source) snd_soc_dapm_widget_for_each_sink_path(w, path)
dapm_widget_set_peer_power(path->sink, power, dapm_widget_set_peer_power(path->sink, power,
path->connect); path->connect);
} }
...@@ -1951,6 +1894,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, ...@@ -1951,6 +1894,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
{ {
struct snd_soc_dapm_widget *w = file->private_data; struct snd_soc_dapm_widget *w = file->private_data;
struct snd_soc_card *card = w->dapm->card; struct snd_soc_card *card = w->dapm->card;
enum snd_soc_dapm_direction dir, rdir;
char *buf; char *buf;
int in, out; int in, out;
ssize_t ret; ssize_t ret;
...@@ -1987,25 +1931,21 @@ static ssize_t dapm_widget_power_read_file(struct file *file, ...@@ -1987,25 +1931,21 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
w->sname, w->sname,
w->active ? "active" : "inactive"); w->active ? "active" : "inactive");
list_for_each_entry(p, &w->sources, list_sink) { snd_soc_dapm_for_each_direction(dir) {
if (p->connected && !p->connected(w, p->source)) rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
continue; snd_soc_dapm_widget_for_each_path(w, dir, p) {
if (p->connected && !p->connected(w, p->node[rdir]))
continue;
if (p->connect) if (!p->connect)
ret += snprintf(buf + ret, PAGE_SIZE - ret, continue;
" in \"%s\" \"%s\"\n",
p->name ? p->name : "static",
p->source->name);
}
list_for_each_entry(p, &w->sinks, list_source) {
if (p->connected && !p->connected(w, p->sink))
continue;
if (p->connect)
ret += snprintf(buf + ret, PAGE_SIZE - ret, ret += snprintf(buf + ret, PAGE_SIZE - ret,
" out \"%s\" \"%s\"\n", " %s \"%s\" \"%s\"\n",
(rdir == SND_SOC_DAPM_DIR_IN) ? "in" : "out",
p->name ? p->name : "static", p->name ? p->name : "static",
p->sink->name); p->node[rdir]->name);
}
} }
mutex_unlock(&card->dapm_mutex); mutex_unlock(&card->dapm_mutex);
...@@ -2305,37 +2245,43 @@ struct attribute *soc_dapm_dev_attrs[] = { ...@@ -2305,37 +2245,43 @@ struct attribute *soc_dapm_dev_attrs[] = {
static void dapm_free_path(struct snd_soc_dapm_path *path) static void dapm_free_path(struct snd_soc_dapm_path *path)
{ {
list_del(&path->list_sink); list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]);
list_del(&path->list_source); list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]);
list_del(&path->list_kcontrol); list_del(&path->list_kcontrol);
list_del(&path->list); list_del(&path->list);
kfree(path); kfree(path);
} }
void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_path *p, *next_p;
enum snd_soc_dapm_direction dir;
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.
*/
snd_soc_dapm_for_each_direction(dir) {
snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p)
dapm_free_path(p);
}
kfree(w->kcontrols);
kfree_const(w->name);
kfree(w);
}
/* free all dapm widgets and resources */ /* free all dapm widgets and resources */
static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
{ {
struct snd_soc_dapm_widget *w, *next_w; 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->card->widgets, list) { list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) {
if (w->dapm != dapm) if (w->dapm != dapm)
continue; continue;
list_del(&w->list); snd_soc_dapm_free_widget(w);
/*
* 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)
dapm_free_path(p);
list_for_each_entry_safe(p, next_p, &w->sinks, list_source)
dapm_free_path(p);
kfree(w->kcontrols);
kfree(w->name);
kfree(w);
} }
} }
...@@ -2441,20 +2387,22 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); ...@@ -2441,20 +2387,22 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
*/ */
static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
{ {
enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_path *p; struct snd_soc_dapm_path *p;
unsigned int ep;
switch (w->id) { switch (w->id) {
case snd_soc_dapm_input: case snd_soc_dapm_input:
/* On a fully routed card a input is never a source */ /* On a fully routed card a input is never a source */
if (w->dapm->card->fully_routed) if (w->dapm->card->fully_routed)
break; return;
w->is_source = 1; ep = SND_SOC_DAPM_EP_SOURCE;
list_for_each_entry(p, &w->sources, list_sink) { snd_soc_dapm_widget_for_each_source_path(w, p) {
if (p->source->id == snd_soc_dapm_micbias || if (p->source->id == snd_soc_dapm_micbias ||
p->source->id == snd_soc_dapm_mic || p->source->id == snd_soc_dapm_mic ||
p->source->id == snd_soc_dapm_line || p->source->id == snd_soc_dapm_line ||
p->source->id == snd_soc_dapm_output) { p->source->id == snd_soc_dapm_output) {
w->is_source = 0; ep = 0;
break; break;
} }
} }
...@@ -2462,25 +2410,30 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) ...@@ -2462,25 +2410,30 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
case snd_soc_dapm_output: case snd_soc_dapm_output:
/* On a fully routed card a output is never a sink */ /* On a fully routed card a output is never a sink */
if (w->dapm->card->fully_routed) if (w->dapm->card->fully_routed)
break; return;
w->is_sink = 1; ep = SND_SOC_DAPM_EP_SINK;
list_for_each_entry(p, &w->sinks, list_source) { snd_soc_dapm_widget_for_each_sink_path(w, p) {
if (p->sink->id == snd_soc_dapm_spk || if (p->sink->id == snd_soc_dapm_spk ||
p->sink->id == snd_soc_dapm_hp || p->sink->id == snd_soc_dapm_hp ||
p->sink->id == snd_soc_dapm_line || p->sink->id == snd_soc_dapm_line ||
p->sink->id == snd_soc_dapm_input) { p->sink->id == snd_soc_dapm_input) {
w->is_sink = 0; ep = 0;
break; break;
} }
} }
break; break;
case snd_soc_dapm_line: case snd_soc_dapm_line:
w->is_sink = !list_empty(&w->sources); ep = 0;
w->is_source = !list_empty(&w->sinks); snd_soc_dapm_for_each_direction(dir) {
if (!list_empty(&w->edges[dir]))
ep |= SND_SOC_DAPM_DIR_TO_EP(dir);
}
break; break;
default: default:
break; return;
} }
w->is_ep = ep;
} }
static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm, static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm,
...@@ -2533,6 +2486,8 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, ...@@ -2533,6 +2486,8 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
int (*connected)(struct snd_soc_dapm_widget *source, int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)) struct snd_soc_dapm_widget *sink))
{ {
struct snd_soc_dapm_widget *widgets[2];
enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_path *path; struct snd_soc_dapm_path *path;
int ret; int ret;
...@@ -2565,13 +2520,14 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, ...@@ -2565,13 +2520,14 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
path->source = wsource; path->node[SND_SOC_DAPM_DIR_IN] = wsource;
path->sink = wsink; path->node[SND_SOC_DAPM_DIR_OUT] = wsink;
widgets[SND_SOC_DAPM_DIR_IN] = wsource;
widgets[SND_SOC_DAPM_DIR_OUT] = wsink;
path->connected = connected; path->connected = connected;
INIT_LIST_HEAD(&path->list); INIT_LIST_HEAD(&path->list);
INIT_LIST_HEAD(&path->list_kcontrol); INIT_LIST_HEAD(&path->list_kcontrol);
INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink);
if (wsource->is_supply || wsink->is_supply) if (wsource->is_supply || wsink->is_supply)
path->is_supply = 1; path->is_supply = 1;
...@@ -2609,14 +2565,13 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, ...@@ -2609,14 +2565,13 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
} }
list_add(&path->list, &dapm->card->paths); list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources); snd_soc_dapm_for_each_direction(dir)
list_add(&path->list_source, &wsource->sinks); list_add(&path->list_node[dir], &widgets[dir]->edges[dir]);
dapm_update_widget_flags(wsource); snd_soc_dapm_for_each_direction(dir) {
dapm_update_widget_flags(wsink); dapm_update_widget_flags(widgets[dir]);
dapm_mark_dirty(widgets[dir], "Route added");
dapm_mark_dirty(wsource, "Route added"); }
dapm_mark_dirty(wsink, "Route added");
if (dapm->card->instantiated && path->connect) if (dapm->card->instantiated && path->connect)
dapm_path_invalidate(path); dapm_path_invalidate(path);
...@@ -2864,7 +2819,7 @@ static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, ...@@ -2864,7 +2819,7 @@ static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n", dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n",
route->source, route->sink); route->source, route->sink);
list_for_each_entry(path, &source->sinks, list_source) { snd_soc_dapm_widget_for_each_sink_path(source, path) {
if (path->sink == sink) { if (path->sink == sink) {
path->weak = 1; path->weak = 1;
count++; count++;
...@@ -3298,6 +3253,7 @@ struct snd_soc_dapm_widget * ...@@ -3298,6 +3253,7 @@ struct snd_soc_dapm_widget *
snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget) const struct snd_soc_dapm_widget *widget)
{ {
enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
const char *prefix; const char *prefix;
int ret; int ret;
...@@ -3344,7 +3300,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, ...@@ -3344,7 +3300,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
if (prefix) if (prefix)
w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name); w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name);
else else
w->name = kasprintf(GFP_KERNEL, "%s", widget->name); w->name = kstrdup_const(widget->name, GFP_KERNEL);
if (w->name == NULL) { if (w->name == NULL) {
kfree(w); kfree(w);
return NULL; return NULL;
...@@ -3352,27 +3308,27 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, ...@@ -3352,27 +3308,27 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
switch (w->id) { switch (w->id) {
case snd_soc_dapm_mic: case snd_soc_dapm_mic:
w->is_source = 1; w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_generic_check_power; w->power_check = dapm_generic_check_power;
break; break;
case snd_soc_dapm_input: case snd_soc_dapm_input:
if (!dapm->card->fully_routed) if (!dapm->card->fully_routed)
w->is_source = 1; w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_generic_check_power; w->power_check = dapm_generic_check_power;
break; break;
case snd_soc_dapm_spk: case snd_soc_dapm_spk:
case snd_soc_dapm_hp: case snd_soc_dapm_hp:
w->is_sink = 1; w->is_ep = SND_SOC_DAPM_EP_SINK;
w->power_check = dapm_generic_check_power; w->power_check = dapm_generic_check_power;
break; break;
case snd_soc_dapm_output: case snd_soc_dapm_output:
if (!dapm->card->fully_routed) if (!dapm->card->fully_routed)
w->is_sink = 1; w->is_ep = SND_SOC_DAPM_EP_SINK;
w->power_check = dapm_generic_check_power; w->power_check = dapm_generic_check_power;
break; break;
case snd_soc_dapm_vmid: case snd_soc_dapm_vmid:
case snd_soc_dapm_siggen: case snd_soc_dapm_siggen:
w->is_source = 1; w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_always_on_check_power; w->power_check = dapm_always_on_check_power;
break; break;
case snd_soc_dapm_mux: case snd_soc_dapm_mux:
...@@ -3406,14 +3362,14 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, ...@@ -3406,14 +3362,14 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
} }
w->dapm = dapm; w->dapm = dapm;
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list); INIT_LIST_HEAD(&w->list);
INIT_LIST_HEAD(&w->dirty); INIT_LIST_HEAD(&w->dirty);
list_add_tail(&w->list, &dapm->card->widgets); list_add_tail(&w->list, &dapm->card->widgets);
w->inputs = -1; snd_soc_dapm_for_each_direction(dir) {
w->outputs = -1; INIT_LIST_HEAD(&w->edges[dir]);
w->endpoints[dir] = -1;
}
/* machine layer set ups unconnected pins and insertions */ /* machine layer set ups unconnected pins and insertions */
w->connected = 1; w->connected = 1;
...@@ -3467,19 +3423,17 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, ...@@ -3467,19 +3423,17 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
int ret; int ret;
if (WARN_ON(!config) || if (WARN_ON(!config) ||
WARN_ON(list_empty(&w->sources) || list_empty(&w->sinks))) WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) ||
list_empty(&w->edges[SND_SOC_DAPM_DIR_IN])))
return -EINVAL; return -EINVAL;
/* We only support a single source and sink, pick the first */ /* We only support a single source and sink, pick the first */
source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path, source_p = list_first_entry(&w->edges[SND_SOC_DAPM_DIR_OUT],
list_sink); struct snd_soc_dapm_path,
sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path, list_node[SND_SOC_DAPM_DIR_OUT]);
list_source); sink_p = list_first_entry(&w->edges[SND_SOC_DAPM_DIR_IN],
struct snd_soc_dapm_path,
if (WARN_ON(!source_p || !sink_p) || list_node[SND_SOC_DAPM_DIR_IN]);
WARN_ON(!sink_p->source || !source_p->sink) ||
WARN_ON(!source_p->source || !sink_p->sink))
return -EINVAL;
source = source_p->source->priv; source = source_p->source->priv;
sink = sink_p->sink->priv; sink = sink_p->sink->priv;
...@@ -3851,6 +3805,7 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, ...@@ -3851,6 +3805,7 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
int event) int event)
{ {
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
unsigned int ep;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) if (stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget; w = dai->playback_widget;
...@@ -3860,12 +3815,22 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, ...@@ -3860,12 +3815,22 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
if (w) { if (w) {
dapm_mark_dirty(w, "stream event"); dapm_mark_dirty(w, "stream event");
if (w->id == snd_soc_dapm_dai_in) {
ep = SND_SOC_DAPM_EP_SOURCE;
dapm_widget_invalidate_input_paths(w);
} else {
ep = SND_SOC_DAPM_EP_SINK;
dapm_widget_invalidate_output_paths(w);
}
switch (event) { switch (event) {
case SND_SOC_DAPM_STREAM_START: case SND_SOC_DAPM_STREAM_START:
w->active = 1; w->active = 1;
w->is_ep = ep;
break; break;
case SND_SOC_DAPM_STREAM_STOP: case SND_SOC_DAPM_STREAM_STOP:
w->active = 0; w->active = 0;
w->is_ep = 0;
break; break;
case SND_SOC_DAPM_STREAM_SUSPEND: case SND_SOC_DAPM_STREAM_SUSPEND:
case SND_SOC_DAPM_STREAM_RESUME: case SND_SOC_DAPM_STREAM_RESUME:
...@@ -3873,14 +3838,6 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, ...@@ -3873,14 +3838,6 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
break; break;
} }
if (w->id == snd_soc_dapm_dai_in) {
w->is_source = w->active;
dapm_widget_invalidate_input_paths(w);
} else {
w->is_sink = w->active;
dapm_widget_invalidate_output_paths(w);
}
} }
} }
......
...@@ -1231,24 +1231,17 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, ...@@ -1231,24 +1231,17 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
} }
int dpcm_path_get(struct snd_soc_pcm_runtime *fe, int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
int stream, struct snd_soc_dapm_widget_list **list_) int stream, struct snd_soc_dapm_widget_list **list)
{ {
struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dai *cpu_dai = fe->cpu_dai;
struct snd_soc_dapm_widget_list *list;
int paths; int paths;
list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) +
sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL);
if (list == NULL)
return -ENOMEM;
/* get number of valid DAI paths and their widgets */ /* get number of valid DAI paths and their widgets */
paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list); paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list);
dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
stream ? "capture" : "playback"); stream ? "capture" : "playback");
*list_ = list;
return paths; return paths;
} }
......
...@@ -1758,7 +1758,6 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, ...@@ -1758,7 +1758,6 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm,
u32 index) u32 index)
{ {
struct snd_soc_dapm_widget *w, *next_w; 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->card->widgets, list) { list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) {
...@@ -1770,31 +1769,9 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, ...@@ -1770,31 +1769,9 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm,
if (w->dobj.index != index && if (w->dobj.index != index &&
w->dobj.index != SND_SOC_TPLG_INDEX_ALL) w->dobj.index != SND_SOC_TPLG_INDEX_ALL)
continue; 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);
}
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);
}
/* check and free and dynamic widget kcontrols */ /* check and free and dynamic widget kcontrols */
snd_soc_tplg_widget_remove(w); snd_soc_tplg_widget_remove(w);
kfree(w->kcontrols); snd_soc_dapm_free_widget(w);
kfree(w->name);
kfree(w);
} }
} }
EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all); EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment