xref: /openbmc/linux/sound/soc/soc-dapm.c (revision 643d1f7f)
1 /*
2  * soc-dapm.c  --  ALSA SoC Dynamic Audio Power Management
3  *
4  * Copyright 2005 Wolfson Microelectronics PLC.
5  * Author: Liam Girdwood
6  *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
7  *
8  *  This program is free software; you can redistribute  it and/or modify it
9  *  under  the terms of  the GNU General  Public License as published by the
10  *  Free Software Foundation;  either version 2 of the  License, or (at your
11  *  option) any later version.
12  *
13  *  Revision history
14  *    12th Aug 2005   Initial version.
15  *    25th Oct 2005   Implemented path power domain.
16  *    18th Dec 2005   Implemented machine and stream level power domain.
17  *
18  *  Features:
19  *    o Changes power status of internal codec blocks depending on the
20  *      dynamic configuration of codec internal audio paths and active
21  *      DAC's/ADC's.
22  *    o Platform power domain - can support external components i.e. amps and
23  *      mic/meadphone insertion events.
24  *    o Automatic Mic Bias support
25  *    o Jack insertion power event initiation - e.g. hp insertion will enable
26  *      sinks, dacs, etc
27  *    o Delayed powerdown of audio susbsystem to reduce pops between a quick
28  *      device reopen.
29  *
30  *  Todo:
31  *    o DAPM power change sequencing - allow for configurable per
32  *      codec sequences.
33  *    o Support for analogue bias optimisation.
34  *    o Support for reduced codec oversampling rates.
35  *    o Support for reduced codec bias currents.
36  */
37 
38 #include <linux/module.h>
39 #include <linux/moduleparam.h>
40 #include <linux/init.h>
41 #include <linux/delay.h>
42 #include <linux/pm.h>
43 #include <linux/bitops.h>
44 #include <linux/platform_device.h>
45 #include <linux/jiffies.h>
46 #include <sound/core.h>
47 #include <sound/pcm.h>
48 #include <sound/pcm_params.h>
49 #include <sound/soc-dapm.h>
50 #include <sound/initval.h>
51 
52 /* debug */
53 #define DAPM_DEBUG 0
54 #if DAPM_DEBUG
55 #define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
56 #define dbg(format, arg...) printk(format, ## arg)
57 #else
58 #define dump_dapm(codec, action)
59 #define dbg(format, arg...)
60 #endif
61 
62 #define POP_DEBUG 0
63 #if POP_DEBUG
64 #define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
65 #define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time))
66 #define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
67 #else
68 #define pop_dbg(format, arg...)
69 #define pop_wait(time)
70 #endif
71 
72 /* dapm power sequences - make this per codec in the future */
73 static int dapm_up_seq[] = {
74 	snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
75 	snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga,
76 	snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
77 };
78 static int dapm_down_seq[] = {
79 	snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
80 	snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
81 	snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post
82 };
83 
84 static int dapm_status = 1;
85 module_param(dapm_status, int, 0);
86 MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
87 
88 /* create a new dapm widget */
89 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
90 	const struct snd_soc_dapm_widget *_widget)
91 {
92 	return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
93 }
94 
95 /* set up initial codec paths */
96 static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
97 	struct snd_soc_dapm_path *p, int i)
98 {
99 	switch (w->id) {
100 	case snd_soc_dapm_switch:
101 	case snd_soc_dapm_mixer: {
102 		int val;
103 		int reg = w->kcontrols[i].private_value & 0xff;
104 		int shift = (w->kcontrols[i].private_value >> 8) & 0x0f;
105 		int mask = (w->kcontrols[i].private_value >> 16) & 0xff;
106 		int invert = (w->kcontrols[i].private_value >> 24) & 0x01;
107 
108 		val = snd_soc_read(w->codec, reg);
109 		val = (val >> shift) & mask;
110 
111 		if ((invert && !val) || (!invert && val))
112 			p->connect = 1;
113 		else
114 			p->connect = 0;
115 	}
116 	break;
117 	case snd_soc_dapm_mux: {
118 		struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
119 		int val, item, bitmask;
120 
121 		for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
122 		;
123 		val = snd_soc_read(w->codec, e->reg);
124 		item = (val >> e->shift_l) & (bitmask - 1);
125 
126 		p->connect = 0;
127 		for (i = 0; i < e->mask; i++) {
128 			if (!(strcmp(p->name, e->texts[i])) && item == i)
129 				p->connect = 1;
130 		}
131 	}
132 	break;
133 	/* does not effect routing - always connected */
134 	case snd_soc_dapm_pga:
135 	case snd_soc_dapm_output:
136 	case snd_soc_dapm_adc:
137 	case snd_soc_dapm_input:
138 	case snd_soc_dapm_dac:
139 	case snd_soc_dapm_micbias:
140 	case snd_soc_dapm_vmid:
141 		p->connect = 1;
142 	break;
143 	/* does effect routing - dynamically connected */
144 	case snd_soc_dapm_hp:
145 	case snd_soc_dapm_mic:
146 	case snd_soc_dapm_spk:
147 	case snd_soc_dapm_line:
148 	case snd_soc_dapm_pre:
149 	case snd_soc_dapm_post:
150 		p->connect = 0;
151 	break;
152 	}
153 }
154 
155 /* connect mux widget to it's interconnecting audio paths */
156 static int dapm_connect_mux(struct snd_soc_codec *codec,
157 	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
158 	struct snd_soc_dapm_path *path, const char *control_name,
159 	const struct snd_kcontrol_new *kcontrol)
160 {
161 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
162 	int i;
163 
164 	for (i = 0; i < e->mask; i++) {
165 		if (!(strcmp(control_name, e->texts[i]))) {
166 			list_add(&path->list, &codec->dapm_paths);
167 			list_add(&path->list_sink, &dest->sources);
168 			list_add(&path->list_source, &src->sinks);
169 			path->name = (char*)e->texts[i];
170 			dapm_set_path_status(dest, path, 0);
171 			return 0;
172 		}
173 	}
174 
175 	return -ENODEV;
176 }
177 
178 /* connect mixer widget to it's interconnecting audio paths */
179 static int dapm_connect_mixer(struct snd_soc_codec *codec,
180 	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
181 	struct snd_soc_dapm_path *path, const char *control_name)
182 {
183 	int i;
184 
185 	/* search for mixer kcontrol */
186 	for (i = 0; i < dest->num_kcontrols; i++) {
187 		if (!strcmp(control_name, dest->kcontrols[i].name)) {
188 			list_add(&path->list, &codec->dapm_paths);
189 			list_add(&path->list_sink, &dest->sources);
190 			list_add(&path->list_source, &src->sinks);
191 			path->name = dest->kcontrols[i].name;
192 			dapm_set_path_status(dest, path, i);
193 			return 0;
194 		}
195 	}
196 	return -ENODEV;
197 }
198 
199 /* update dapm codec register bits */
200 static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
201 {
202 	int change, power;
203 	unsigned short old, new;
204 	struct snd_soc_codec *codec = widget->codec;
205 
206 	/* check for valid widgets */
207 	if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
208 		widget->id == snd_soc_dapm_output ||
209 		widget->id == snd_soc_dapm_hp ||
210 		widget->id == snd_soc_dapm_mic ||
211 		widget->id == snd_soc_dapm_line ||
212 		widget->id == snd_soc_dapm_spk)
213 		return 0;
214 
215 	power = widget->power;
216 	if (widget->invert)
217 		power = (power ? 0:1);
218 
219 	old = snd_soc_read(codec, widget->reg);
220 	new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
221 
222 	change = old != new;
223 	if (change) {
224 		pop_dbg("pop test %s : %s in %d ms\n", widget->name,
225 			widget->power ? "on" : "off", POP_TIME);
226 		snd_soc_write(codec, widget->reg, new);
227 		pop_wait(POP_TIME);
228 	}
229 	dbg("reg old %x new %x change %d\n", old, new, change);
230 	return change;
231 }
232 
233 /* ramps the volume up or down to minimise pops before or after a
234  * DAPM power event */
235 static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)
236 {
237 	const struct snd_kcontrol_new *k = widget->kcontrols;
238 
239 	if (widget->muted && !power)
240 		return 0;
241 	if (!widget->muted && power)
242 		return 0;
243 
244 	if (widget->num_kcontrols && k) {
245 		int reg = k->private_value & 0xff;
246 		int shift = (k->private_value >> 8) & 0x0f;
247 		int mask = (k->private_value >> 16) & 0xff;
248 		int invert = (k->private_value >> 24) & 0x01;
249 
250 		if (power) {
251 			int i;
252 			/* power up has happended, increase volume to last level */
253 			if (invert) {
254 				for (i = mask; i > widget->saved_value; i--)
255 					snd_soc_update_bits(widget->codec, reg, mask, i);
256 			} else {
257 				for (i = 0; i < widget->saved_value; i++)
258 					snd_soc_update_bits(widget->codec, reg, mask, i);
259 			}
260 			widget->muted = 0;
261 		} else {
262 			/* power down is about to occur, decrease volume to mute */
263 			int val = snd_soc_read(widget->codec, reg);
264 			int i = widget->saved_value = (val >> shift) & mask;
265 			if (invert) {
266 				for (; i < mask; i++)
267 					snd_soc_update_bits(widget->codec, reg, mask, i);
268 			} else {
269 				for (; i > 0; i--)
270 					snd_soc_update_bits(widget->codec, reg, mask, i);
271 			}
272 			widget->muted = 1;
273 		}
274 	}
275 	return 0;
276 }
277 
278 /* create new dapm mixer control */
279 static int dapm_new_mixer(struct snd_soc_codec *codec,
280 	struct snd_soc_dapm_widget *w)
281 {
282 	int i, ret = 0;
283 	char name[32];
284 	struct snd_soc_dapm_path *path;
285 
286 	/* add kcontrol */
287 	for (i = 0; i < w->num_kcontrols; i++) {
288 
289 		/* match name */
290 		list_for_each_entry(path, &w->sources, list_sink) {
291 
292 			/* mixer/mux paths name must match control name */
293 			if (path->name != (char*)w->kcontrols[i].name)
294 				continue;
295 
296 			/* add dapm control with long name */
297 			snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
298 			path->long_name = kstrdup (name, GFP_KERNEL);
299 			if (path->long_name == NULL)
300 				return -ENOMEM;
301 
302 			path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
303 				path->long_name);
304 			ret = snd_ctl_add(codec->card, path->kcontrol);
305 			if (ret < 0) {
306 				printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n",
307 						path->long_name);
308 				kfree(path->long_name);
309 				path->long_name = NULL;
310 				return ret;
311 			}
312 		}
313 	}
314 	return ret;
315 }
316 
317 /* create new dapm mux control */
318 static int dapm_new_mux(struct snd_soc_codec *codec,
319 	struct snd_soc_dapm_widget *w)
320 {
321 	struct snd_soc_dapm_path *path = NULL;
322 	struct snd_kcontrol *kcontrol;
323 	int ret = 0;
324 
325 	if (!w->num_kcontrols) {
326 		printk(KERN_ERR "asoc: mux %s has no controls\n", w->name);
327 		return -EINVAL;
328 	}
329 
330 	kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
331 	ret = snd_ctl_add(codec->card, kcontrol);
332 	if (ret < 0)
333 		goto err;
334 
335 	list_for_each_entry(path, &w->sources, list_sink)
336 		path->kcontrol = kcontrol;
337 
338 	return ret;
339 
340 err:
341 	printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
342 	return ret;
343 }
344 
345 /* create new dapm volume control */
346 static int dapm_new_pga(struct snd_soc_codec *codec,
347 	struct snd_soc_dapm_widget *w)
348 {
349 	struct snd_kcontrol *kcontrol;
350 	int ret = 0;
351 
352 	if (!w->num_kcontrols)
353 		return -EINVAL;
354 
355 	kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
356 	ret = snd_ctl_add(codec->card, kcontrol);
357 	if (ret < 0) {
358 		printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
359 		return ret;
360 	}
361 
362 	return ret;
363 }
364 
365 /* reset 'walked' bit for each dapm path */
366 static inline void dapm_clear_walk(struct snd_soc_codec *codec)
367 {
368 	struct snd_soc_dapm_path *p;
369 
370 	list_for_each_entry(p, &codec->dapm_paths, list)
371 		p->walked = 0;
372 }
373 
374 /*
375  * Recursively check for a completed path to an active or physically connected
376  * output widget. Returns number of complete paths.
377  */
378 static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
379 {
380 	struct snd_soc_dapm_path *path;
381 	int con = 0;
382 
383 	if (widget->id == snd_soc_dapm_adc && widget->active)
384 		return 1;
385 
386 	if (widget->connected) {
387 		/* connected pin ? */
388 		if (widget->id == snd_soc_dapm_output && !widget->ext)
389 			return 1;
390 
391 		/* connected jack or spk ? */
392 		if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk ||
393 			widget->id == snd_soc_dapm_line)
394 			return 1;
395 	}
396 
397 	list_for_each_entry(path, &widget->sinks, list_source) {
398 		if (path->walked)
399 			continue;
400 
401 		if (path->sink && path->connect) {
402 			path->walked = 1;
403 			con += is_connected_output_ep(path->sink);
404 		}
405 	}
406 
407 	return con;
408 }
409 
410 /*
411  * Recursively check for a completed path to an active or physically connected
412  * input widget. Returns number of complete paths.
413  */
414 static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
415 {
416 	struct snd_soc_dapm_path *path;
417 	int con = 0;
418 
419 	/* active stream ? */
420 	if (widget->id == snd_soc_dapm_dac && widget->active)
421 		return 1;
422 
423 	if (widget->connected) {
424 		/* connected pin ? */
425 		if (widget->id == snd_soc_dapm_input && !widget->ext)
426 			return 1;
427 
428 		/* connected VMID/Bias for lower pops */
429 		if (widget->id == snd_soc_dapm_vmid)
430 			return 1;
431 
432 		/* connected jack ? */
433 		if (widget->id == snd_soc_dapm_mic || widget->id == snd_soc_dapm_line)
434 			return 1;
435 	}
436 
437 	list_for_each_entry(path, &widget->sources, list_sink) {
438 		if (path->walked)
439 			continue;
440 
441 		if (path->source && path->connect) {
442 			path->walked = 1;
443 			con += is_connected_input_ep(path->source);
444 		}
445 	}
446 
447 	return con;
448 }
449 
450 /*
451  * Scan each dapm widget for complete audio path.
452  * A complete path is a route that has valid endpoints i.e.:-
453  *
454  *  o DAC to output pin.
455  *  o Input Pin to ADC.
456  *  o Input pin to Output pin (bypass, sidetone)
457  *  o DAC to ADC (loopback).
458  */
459 static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
460 {
461 	struct snd_soc_dapm_widget *w;
462 	int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power;
463 
464 	/* do we have a sequenced stream event */
465 	if (event == SND_SOC_DAPM_STREAM_START) {
466 		c = ARRAY_SIZE(dapm_up_seq);
467 		seq = dapm_up_seq;
468 	} else if (event == SND_SOC_DAPM_STREAM_STOP) {
469 		c = ARRAY_SIZE(dapm_down_seq);
470 		seq = dapm_down_seq;
471 	}
472 
473 	for(i = 0; i < c; i++) {
474 		list_for_each_entry(w, &codec->dapm_widgets, list) {
475 
476 			/* is widget in stream order */
477 			if (seq && seq[i] && w->id != seq[i])
478 				continue;
479 
480 			/* vmid - no action */
481 			if (w->id == snd_soc_dapm_vmid)
482 				continue;
483 
484 			/* active ADC */
485 			if (w->id == snd_soc_dapm_adc && w->active) {
486 				in = is_connected_input_ep(w);
487 				dapm_clear_walk(w->codec);
488 				w->power = (in != 0) ? 1 : 0;
489 				dapm_update_bits(w);
490 				continue;
491 			}
492 
493 			/* active DAC */
494 			if (w->id == snd_soc_dapm_dac && w->active) {
495 				out = is_connected_output_ep(w);
496 				dapm_clear_walk(w->codec);
497 				w->power = (out != 0) ? 1 : 0;
498 				dapm_update_bits(w);
499 				continue;
500 			}
501 
502 			/* programmable gain/attenuation */
503 			if (w->id == snd_soc_dapm_pga) {
504 				int on;
505 				in = is_connected_input_ep(w);
506 				dapm_clear_walk(w->codec);
507 				out = is_connected_output_ep(w);
508 				dapm_clear_walk(w->codec);
509 				w->power = on = (out != 0 && in != 0) ? 1 : 0;
510 
511 				if (!on)
512 					dapm_set_pga(w, on); /* lower volume to reduce pops */
513 				dapm_update_bits(w);
514 				if (on)
515 					dapm_set_pga(w, on); /* restore volume from zero */
516 
517 				continue;
518 			}
519 
520 			/* pre and post event widgets */
521 			if (w->id == snd_soc_dapm_pre) {
522 				if (!w->event)
523 					continue;
524 
525 				if (event == SND_SOC_DAPM_STREAM_START) {
526 					ret = w->event(w,
527 						NULL, SND_SOC_DAPM_PRE_PMU);
528 					if (ret < 0)
529 						return ret;
530 				} else if (event == SND_SOC_DAPM_STREAM_STOP) {
531 					ret = w->event(w,
532 						NULL, SND_SOC_DAPM_PRE_PMD);
533 					if (ret < 0)
534 						return ret;
535 				}
536 				continue;
537 			}
538 			if (w->id == snd_soc_dapm_post) {
539 				if (!w->event)
540 					continue;
541 
542 				if (event == SND_SOC_DAPM_STREAM_START) {
543 					ret = w->event(w,
544 						NULL, SND_SOC_DAPM_POST_PMU);
545 					if (ret < 0)
546 						return ret;
547 				} else if (event == SND_SOC_DAPM_STREAM_STOP) {
548 					ret = w->event(w,
549 						NULL, SND_SOC_DAPM_POST_PMD);
550 					if (ret < 0)
551 						return ret;
552 				}
553 				continue;
554 			}
555 
556 			/* all other widgets */
557 			in = is_connected_input_ep(w);
558 			dapm_clear_walk(w->codec);
559 			out = is_connected_output_ep(w);
560 			dapm_clear_walk(w->codec);
561 			power = (out != 0 && in != 0) ? 1 : 0;
562 			power_change = (w->power == power) ? 0: 1;
563 			w->power = power;
564 
565 			/* call any power change event handlers */
566 			if (power_change) {
567 				if (w->event) {
568 					dbg("power %s event for %s flags %x\n",
569 						w->power ? "on" : "off", w->name, w->event_flags);
570 					if (power) {
571 						/* power up event */
572 						if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
573 							ret = w->event(w,
574 								NULL, SND_SOC_DAPM_PRE_PMU);
575 							if (ret < 0)
576 								return ret;
577 						}
578 						dapm_update_bits(w);
579 						if (w->event_flags & SND_SOC_DAPM_POST_PMU){
580 							ret = w->event(w,
581 								NULL, SND_SOC_DAPM_POST_PMU);
582 							if (ret < 0)
583 								return ret;
584 						}
585 					} else {
586 						/* power down event */
587 						if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
588 							ret = w->event(w,
589 								NULL, SND_SOC_DAPM_PRE_PMD);
590 							if (ret < 0)
591 								return ret;
592 						}
593 						dapm_update_bits(w);
594 						if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
595 							ret = w->event(w,
596 								NULL, SND_SOC_DAPM_POST_PMD);
597 							if (ret < 0)
598 								return ret;
599 						}
600 					}
601 				} else
602 					/* no event handler */
603 					dapm_update_bits(w);
604 			}
605 		}
606 	}
607 
608 	return ret;
609 }
610 
611 #if DAPM_DEBUG
612 static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
613 {
614 	struct snd_soc_dapm_widget *w;
615 	struct snd_soc_dapm_path *p = NULL;
616 	int in, out;
617 
618 	printk("DAPM %s %s\n", codec->name, action);
619 
620 	list_for_each_entry(w, &codec->dapm_widgets, list) {
621 
622 		/* only display widgets that effect routing */
623 		switch (w->id) {
624 		case snd_soc_dapm_pre:
625 		case snd_soc_dapm_post:
626 		case snd_soc_dapm_vmid:
627 			continue;
628 		case snd_soc_dapm_mux:
629 		case snd_soc_dapm_output:
630 		case snd_soc_dapm_input:
631 		case snd_soc_dapm_switch:
632 		case snd_soc_dapm_hp:
633 		case snd_soc_dapm_mic:
634 		case snd_soc_dapm_spk:
635 		case snd_soc_dapm_line:
636 		case snd_soc_dapm_micbias:
637 		case snd_soc_dapm_dac:
638 		case snd_soc_dapm_adc:
639 		case snd_soc_dapm_pga:
640 		case snd_soc_dapm_mixer:
641 			if (w->name) {
642 				in = is_connected_input_ep(w);
643 				dapm_clear_walk(w->codec);
644 				out = is_connected_output_ep(w);
645 				dapm_clear_walk(w->codec);
646 				printk("%s: %s  in %d out %d\n", w->name,
647 					w->power ? "On":"Off",in, out);
648 
649 				list_for_each_entry(p, &w->sources, list_sink) {
650 					if (p->connect)
651 						printk(" in  %s %s\n", p->name ? p->name : "static",
652 							p->source->name);
653 				}
654 				list_for_each_entry(p, &w->sinks, list_source) {
655 					if (p->connect)
656 						printk(" out %s %s\n", p->name ? p->name : "static",
657 							p->sink->name);
658 				}
659 			}
660 		break;
661 		}
662 	}
663 }
664 #endif
665 
666 /* test and update the power status of a mux widget */
667 static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
668 				 struct snd_kcontrol *kcontrol, int mask,
669 				 int val, struct soc_enum* e)
670 {
671 	struct snd_soc_dapm_path *path;
672 	int found = 0;
673 
674 	if (widget->id != snd_soc_dapm_mux)
675 		return -ENODEV;
676 
677 	if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
678 		return 0;
679 
680 	/* find dapm widget path assoc with kcontrol */
681 	list_for_each_entry(path, &widget->codec->dapm_paths, list) {
682 		if (path->kcontrol != kcontrol)
683 			continue;
684 
685 		if (!path->name || ! e->texts[val])
686 			continue;
687 
688 		found = 1;
689 		/* we now need to match the string in the enum to the path */
690 		if (!(strcmp(path->name, e->texts[val])))
691 			path->connect = 1; /* new connection */
692 		else
693 			path->connect = 0; /* old connection must be powered down */
694 	}
695 
696 	if (found)
697 		dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
698 
699 	return 0;
700 }
701 
702 /* test and update the power status of a mixer or switch widget */
703 static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
704 				   struct snd_kcontrol *kcontrol, int reg,
705 				   int val_mask, int val, int invert)
706 {
707 	struct snd_soc_dapm_path *path;
708 	int found = 0;
709 
710 	if (widget->id != snd_soc_dapm_mixer &&
711 	    widget->id != snd_soc_dapm_switch)
712 		return -ENODEV;
713 
714 	if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
715 		return 0;
716 
717 	/* find dapm widget path assoc with kcontrol */
718 	list_for_each_entry(path, &widget->codec->dapm_paths, list) {
719 		if (path->kcontrol != kcontrol)
720 			continue;
721 
722 		/* found, now check type */
723 		found = 1;
724 		if (val)
725 			/* new connection */
726 			path->connect = invert ? 0:1;
727 		else
728 			/* old connection must be powered down */
729 			path->connect = invert ? 1:0;
730 		break;
731 	}
732 
733 	if (found)
734 		dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
735 
736 	return 0;
737 }
738 
739 /* show dapm widget status in sys fs */
740 static ssize_t dapm_widget_show(struct device *dev,
741 	struct device_attribute *attr, char *buf)
742 {
743 	struct snd_soc_device *devdata = dev_get_drvdata(dev);
744 	struct snd_soc_codec *codec = devdata->codec;
745 	struct snd_soc_dapm_widget *w;
746 	int count = 0;
747 	char *state = "not set";
748 
749 	list_for_each_entry(w, &codec->dapm_widgets, list) {
750 
751 		/* only display widgets that burnm power */
752 		switch (w->id) {
753 		case snd_soc_dapm_hp:
754 		case snd_soc_dapm_mic:
755 		case snd_soc_dapm_spk:
756 		case snd_soc_dapm_line:
757 		case snd_soc_dapm_micbias:
758 		case snd_soc_dapm_dac:
759 		case snd_soc_dapm_adc:
760 		case snd_soc_dapm_pga:
761 		case snd_soc_dapm_mixer:
762 			if (w->name)
763 				count += sprintf(buf + count, "%s: %s\n",
764 					w->name, w->power ? "On":"Off");
765 		break;
766 		default:
767 		break;
768 		}
769 	}
770 
771 	switch(codec->dapm_state){
772 	case SNDRV_CTL_POWER_D0:
773 		state = "D0";
774 		break;
775 	case SNDRV_CTL_POWER_D1:
776 		state = "D1";
777 		break;
778 	case SNDRV_CTL_POWER_D2:
779 		state = "D2";
780 		break;
781 	case SNDRV_CTL_POWER_D3hot:
782 		state = "D3hot";
783 		break;
784 	case SNDRV_CTL_POWER_D3cold:
785 		state = "D3cold";
786 		break;
787 	}
788 	count += sprintf(buf + count, "PM State: %s\n", state);
789 
790 	return count;
791 }
792 
793 static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
794 
795 int snd_soc_dapm_sys_add(struct device *dev)
796 {
797 	int ret = 0;
798 
799 	if (dapm_status)
800 		ret = device_create_file(dev, &dev_attr_dapm_widget);
801 
802 	return ret;
803 }
804 
805 static void snd_soc_dapm_sys_remove(struct device *dev)
806 {
807 	if (dapm_status)
808 		device_remove_file(dev, &dev_attr_dapm_widget);
809 }
810 
811 /* free all dapm widgets and resources */
812 static void dapm_free_widgets(struct snd_soc_codec *codec)
813 {
814 	struct snd_soc_dapm_widget *w, *next_w;
815 	struct snd_soc_dapm_path *p, *next_p;
816 
817 	list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) {
818 		list_del(&w->list);
819 		kfree(w);
820 	}
821 
822 	list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) {
823 		list_del(&p->list);
824 		kfree(p->long_name);
825 		kfree(p);
826 	}
827 }
828 
829 /**
830  * snd_soc_dapm_sync_endpoints - scan and power dapm paths
831  * @codec: audio codec
832  *
833  * Walks all dapm audio paths and powers widgets according to their
834  * stream or path usage.
835  *
836  * Returns 0 for success.
837  */
838 int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
839 {
840 	return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
841 }
842 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
843 
844 /**
845  * snd_soc_dapm_connect_input - connect dapm widgets
846  * @codec: audio codec
847  * @sink: name of target widget
848  * @control: mixer control name
849  * @source: name of source name
850  *
851  * Connects 2 dapm widgets together via a named audio path. The sink is
852  * the widget receiving the audio signal, whilst the source is the sender
853  * of the audio signal.
854  *
855  * Returns 0 for success else error.
856  */
857 int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
858 	const char * control, const char *source)
859 {
860 	struct snd_soc_dapm_path *path;
861 	struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
862 	int ret = 0;
863 
864 	/* find src and dest widgets */
865 	list_for_each_entry(w, &codec->dapm_widgets, list) {
866 
867 		if (!wsink && !(strcmp(w->name, sink))) {
868 			wsink = w;
869 			continue;
870 		}
871 		if (!wsource && !(strcmp(w->name, source))) {
872 			wsource = w;
873 		}
874 	}
875 
876 	if (wsource == NULL || wsink == NULL)
877 		return -ENODEV;
878 
879 	path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
880 	if (!path)
881 		return -ENOMEM;
882 
883 	path->source = wsource;
884 	path->sink = wsink;
885 	INIT_LIST_HEAD(&path->list);
886 	INIT_LIST_HEAD(&path->list_source);
887 	INIT_LIST_HEAD(&path->list_sink);
888 
889 	/* check for external widgets */
890 	if (wsink->id == snd_soc_dapm_input) {
891 		if (wsource->id == snd_soc_dapm_micbias ||
892 			wsource->id == snd_soc_dapm_mic ||
893 			wsink->id == snd_soc_dapm_line ||
894 			wsink->id == snd_soc_dapm_output)
895 			wsink->ext = 1;
896 	}
897 	if (wsource->id == snd_soc_dapm_output) {
898 		if (wsink->id == snd_soc_dapm_spk ||
899 			wsink->id == snd_soc_dapm_hp ||
900 			wsink->id == snd_soc_dapm_line ||
901 			wsink->id == snd_soc_dapm_input)
902 			wsource->ext = 1;
903 	}
904 
905 	/* connect static paths */
906 	if (control == NULL) {
907 		list_add(&path->list, &codec->dapm_paths);
908 		list_add(&path->list_sink, &wsink->sources);
909 		list_add(&path->list_source, &wsource->sinks);
910 		path->connect = 1;
911 		return 0;
912 	}
913 
914 	/* connect dynamic paths */
915 	switch(wsink->id) {
916 	case snd_soc_dapm_adc:
917 	case snd_soc_dapm_dac:
918 	case snd_soc_dapm_pga:
919 	case snd_soc_dapm_input:
920 	case snd_soc_dapm_output:
921 	case snd_soc_dapm_micbias:
922 	case snd_soc_dapm_vmid:
923 	case snd_soc_dapm_pre:
924 	case snd_soc_dapm_post:
925 		list_add(&path->list, &codec->dapm_paths);
926 		list_add(&path->list_sink, &wsink->sources);
927 		list_add(&path->list_source, &wsource->sinks);
928 		path->connect = 1;
929 		return 0;
930 	case snd_soc_dapm_mux:
931 		ret = dapm_connect_mux(codec, wsource, wsink, path, control,
932 			&wsink->kcontrols[0]);
933 		if (ret != 0)
934 			goto err;
935 		break;
936 	case snd_soc_dapm_switch:
937 	case snd_soc_dapm_mixer:
938 		ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
939 		if (ret != 0)
940 			goto err;
941 		break;
942 	case snd_soc_dapm_hp:
943 	case snd_soc_dapm_mic:
944 	case snd_soc_dapm_line:
945 	case snd_soc_dapm_spk:
946 		list_add(&path->list, &codec->dapm_paths);
947 		list_add(&path->list_sink, &wsink->sources);
948 		list_add(&path->list_source, &wsource->sinks);
949 		path->connect = 0;
950 		return 0;
951 	}
952 	return 0;
953 
954 err:
955 	printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
956 		control, sink);
957 	kfree(path);
958 	return ret;
959 }
960 EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
961 
962 /**
963  * snd_soc_dapm_new_widgets - add new dapm widgets
964  * @codec: audio codec
965  *
966  * Checks the codec for any new dapm widgets and creates them if found.
967  *
968  * Returns 0 for success.
969  */
970 int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
971 {
972 	struct snd_soc_dapm_widget *w;
973 
974 	list_for_each_entry(w, &codec->dapm_widgets, list)
975 	{
976 		if (w->new)
977 			continue;
978 
979 		switch(w->id) {
980 		case snd_soc_dapm_switch:
981 		case snd_soc_dapm_mixer:
982 			dapm_new_mixer(codec, w);
983 			break;
984 		case snd_soc_dapm_mux:
985 			dapm_new_mux(codec, w);
986 			break;
987 		case snd_soc_dapm_adc:
988 		case snd_soc_dapm_dac:
989 		case snd_soc_dapm_pga:
990 			dapm_new_pga(codec, w);
991 			break;
992 		case snd_soc_dapm_input:
993 		case snd_soc_dapm_output:
994 		case snd_soc_dapm_micbias:
995 		case snd_soc_dapm_spk:
996 		case snd_soc_dapm_hp:
997 		case snd_soc_dapm_mic:
998 		case snd_soc_dapm_line:
999 		case snd_soc_dapm_vmid:
1000 		case snd_soc_dapm_pre:
1001 		case snd_soc_dapm_post:
1002 			break;
1003 		}
1004 		w->new = 1;
1005 	}
1006 
1007 	dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
1008 	return 0;
1009 }
1010 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
1011 
1012 /**
1013  * snd_soc_dapm_get_volsw - dapm mixer get callback
1014  * @kcontrol: mixer control
1015  * @uinfo: control element information
1016  *
1017  * Callback to get the value of a dapm mixer control.
1018  *
1019  * Returns 0 for success.
1020  */
1021 int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
1022 	struct snd_ctl_elem_value *ucontrol)
1023 {
1024 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1025 	int reg = kcontrol->private_value & 0xff;
1026 	int shift = (kcontrol->private_value >> 8) & 0x0f;
1027 	int rshift = (kcontrol->private_value >> 12) & 0x0f;
1028 	int max = (kcontrol->private_value >> 16) & 0xff;
1029 	int invert = (kcontrol->private_value >> 24) & 0x01;
1030 	int mask = (1 << fls(max)) - 1;
1031 
1032 	/* return the saved value if we are powered down */
1033 	if (widget->id == snd_soc_dapm_pga && !widget->power) {
1034 		ucontrol->value.integer.value[0] = widget->saved_value;
1035 		return 0;
1036 	}
1037 
1038 	ucontrol->value.integer.value[0] =
1039 		(snd_soc_read(widget->codec, reg) >> shift) & mask;
1040 	if (shift != rshift)
1041 		ucontrol->value.integer.value[1] =
1042 			(snd_soc_read(widget->codec, reg) >> rshift) & mask;
1043 	if (invert) {
1044 		ucontrol->value.integer.value[0] =
1045 			max - ucontrol->value.integer.value[0];
1046 		if (shift != rshift)
1047 			ucontrol->value.integer.value[1] =
1048 				max - ucontrol->value.integer.value[1];
1049 	}
1050 
1051 	return 0;
1052 }
1053 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
1054 
1055 /**
1056  * snd_soc_dapm_put_volsw - dapm mixer set callback
1057  * @kcontrol: mixer control
1058  * @uinfo: control element information
1059  *
1060  * Callback to set the value of a dapm mixer control.
1061  *
1062  * Returns 0 for success.
1063  */
1064 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
1065 	struct snd_ctl_elem_value *ucontrol)
1066 {
1067 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1068 	int reg = kcontrol->private_value & 0xff;
1069 	int shift = (kcontrol->private_value >> 8) & 0x0f;
1070 	int rshift = (kcontrol->private_value >> 12) & 0x0f;
1071 	int max = (kcontrol->private_value >> 16) & 0xff;
1072 	int mask = (1 << fls(max)) - 1;
1073 	int invert = (kcontrol->private_value >> 24) & 0x01;
1074 	unsigned short val, val2, val_mask;
1075 	int ret;
1076 
1077 	val = (ucontrol->value.integer.value[0] & mask);
1078 
1079 	if (invert)
1080 		val = max - val;
1081 	val_mask = mask << shift;
1082 	val = val << shift;
1083 	if (shift != rshift) {
1084 		val2 = (ucontrol->value.integer.value[1] & mask);
1085 		if (invert)
1086 			val2 = max - val2;
1087 		val_mask |= mask << rshift;
1088 		val |= val2 << rshift;
1089 	}
1090 
1091 	mutex_lock(&widget->codec->mutex);
1092 	widget->value = val;
1093 
1094 	/* save volume value if the widget is powered down */
1095 	if (widget->id == snd_soc_dapm_pga && !widget->power) {
1096 		widget->saved_value = val;
1097 		mutex_unlock(&widget->codec->mutex);
1098 		return 1;
1099 	}
1100 
1101 	dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
1102 	if (widget->event) {
1103 		if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
1104 			ret = widget->event(widget, kcontrol,
1105 						SND_SOC_DAPM_PRE_REG);
1106 			if (ret < 0) {
1107 				ret = 1;
1108 				goto out;
1109 			}
1110 		}
1111 		ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1112 		if (widget->event_flags & SND_SOC_DAPM_POST_REG)
1113 			ret = widget->event(widget, kcontrol,
1114 						SND_SOC_DAPM_POST_REG);
1115 	} else
1116 		ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1117 
1118 out:
1119 	mutex_unlock(&widget->codec->mutex);
1120 	return ret;
1121 }
1122 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
1123 
1124 /**
1125  * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback
1126  * @kcontrol: mixer control
1127  * @uinfo: control element information
1128  *
1129  * Callback to get the value of a dapm enumerated double mixer control.
1130  *
1131  * Returns 0 for success.
1132  */
1133 int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
1134 	struct snd_ctl_elem_value *ucontrol)
1135 {
1136 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1137 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1138 	unsigned short val, bitmask;
1139 
1140 	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1141 		;
1142 	val = snd_soc_read(widget->codec, e->reg);
1143 	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
1144 	if (e->shift_l != e->shift_r)
1145 		ucontrol->value.enumerated.item[1] =
1146 			(val >> e->shift_r) & (bitmask - 1);
1147 
1148 	return 0;
1149 }
1150 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
1151 
1152 /**
1153  * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
1154  * @kcontrol: mixer control
1155  * @uinfo: control element information
1156  *
1157  * Callback to set the value of a dapm enumerated double mixer control.
1158  *
1159  * Returns 0 for success.
1160  */
1161 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
1162 	struct snd_ctl_elem_value *ucontrol)
1163 {
1164 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1165 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1166 	unsigned short val, mux;
1167 	unsigned short mask, bitmask;
1168 	int ret = 0;
1169 
1170 	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1171 		;
1172 	if (ucontrol->value.enumerated.item[0] > e->mask - 1)
1173 		return -EINVAL;
1174 	mux = ucontrol->value.enumerated.item[0];
1175 	val = mux << e->shift_l;
1176 	mask = (bitmask - 1) << e->shift_l;
1177 	if (e->shift_l != e->shift_r) {
1178 		if (ucontrol->value.enumerated.item[1] > e->mask - 1)
1179 			return -EINVAL;
1180 		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
1181 		mask |= (bitmask - 1) << e->shift_r;
1182 	}
1183 
1184 	mutex_lock(&widget->codec->mutex);
1185 	widget->value = val;
1186 	dapm_mux_update_power(widget, kcontrol, mask, mux, e);
1187 	if (widget->event) {
1188 		if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
1189 			ret = widget->event(widget,
1190 				kcontrol, SND_SOC_DAPM_PRE_REG);
1191 			if (ret < 0)
1192 				goto out;
1193 		}
1194 		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1195 		if (widget->event_flags & SND_SOC_DAPM_POST_REG)
1196 			ret = widget->event(widget,
1197 				kcontrol, SND_SOC_DAPM_POST_REG);
1198 	} else
1199 		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1200 
1201 out:
1202 	mutex_unlock(&widget->codec->mutex);
1203 	return ret;
1204 }
1205 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
1206 
1207 /**
1208  * snd_soc_dapm_new_control - create new dapm control
1209  * @codec: audio codec
1210  * @widget: widget template
1211  *
1212  * Creates a new dapm control based upon the template.
1213  *
1214  * Returns 0 for success else error.
1215  */
1216 int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
1217 	const struct snd_soc_dapm_widget *widget)
1218 {
1219 	struct snd_soc_dapm_widget *w;
1220 
1221 	if ((w = dapm_cnew_widget(widget)) == NULL)
1222 		return -ENOMEM;
1223 
1224 	w->codec = codec;
1225 	INIT_LIST_HEAD(&w->sources);
1226 	INIT_LIST_HEAD(&w->sinks);
1227 	INIT_LIST_HEAD(&w->list);
1228 	list_add(&w->list, &codec->dapm_widgets);
1229 
1230 	/* machine layer set ups unconnected pins and insertions */
1231 	w->connected = 1;
1232 	return 0;
1233 }
1234 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
1235 
1236 /**
1237  * snd_soc_dapm_stream_event - send a stream event to the dapm core
1238  * @codec: audio codec
1239  * @stream: stream name
1240  * @event: stream event
1241  *
1242  * Sends a stream event to the dapm core. The core then makes any
1243  * necessary widget power changes.
1244  *
1245  * Returns 0 for success else error.
1246  */
1247 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
1248 	char *stream, int event)
1249 {
1250 	struct snd_soc_dapm_widget *w;
1251 
1252 	if (stream == NULL)
1253 		return 0;
1254 
1255 	mutex_lock(&codec->mutex);
1256 	list_for_each_entry(w, &codec->dapm_widgets, list)
1257 	{
1258 		if (!w->sname)
1259 			continue;
1260 		dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
1261 			stream, event);
1262 		if (strstr(w->sname, stream)) {
1263 			switch(event) {
1264 			case SND_SOC_DAPM_STREAM_START:
1265 				w->active = 1;
1266 				break;
1267 			case SND_SOC_DAPM_STREAM_STOP:
1268 				w->active = 0;
1269 				break;
1270 			case SND_SOC_DAPM_STREAM_SUSPEND:
1271 				if (w->active)
1272 					w->suspend = 1;
1273 				w->active = 0;
1274 				break;
1275 			case SND_SOC_DAPM_STREAM_RESUME:
1276 				if (w->suspend) {
1277 					w->active = 1;
1278 					w->suspend = 0;
1279 				}
1280 				break;
1281 			case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
1282 				break;
1283 			case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
1284 				break;
1285 			}
1286 		}
1287 	}
1288 	mutex_unlock(&codec->mutex);
1289 
1290 	dapm_power_widgets(codec, event);
1291 	dump_dapm(codec, __FUNCTION__);
1292 	return 0;
1293 }
1294 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
1295 
1296 /**
1297  * snd_soc_dapm_device_event - send a device event to the dapm core
1298  * @socdev: audio device
1299  * @event: device event
1300  *
1301  * Sends a device event to the dapm core. The core then makes any
1302  * necessary machine or codec power changes..
1303  *
1304  * Returns 0 for success else error.
1305  */
1306 int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event)
1307 {
1308 	struct snd_soc_codec *codec = socdev->codec;
1309 	struct snd_soc_machine *machine = socdev->machine;
1310 
1311 	if (machine->dapm_event)
1312 		machine->dapm_event(machine, event);
1313 	if (codec->dapm_event)
1314 		codec->dapm_event(codec, event);
1315 	return 0;
1316 }
1317 EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
1318 
1319 /**
1320  * snd_soc_dapm_set_endpoint - set audio endpoint status
1321  * @codec: audio codec
1322  * @endpoint: audio signal endpoint (or start point)
1323  * @status: point status
1324  *
1325  * Set audio endpoint status - connected or disconnected.
1326  *
1327  * Returns 0 for success else error.
1328  */
1329 int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
1330 	char *endpoint, int status)
1331 {
1332 	struct snd_soc_dapm_widget *w;
1333 
1334 	list_for_each_entry(w, &codec->dapm_widgets, list) {
1335 		if (!strcmp(w->name, endpoint)) {
1336 			w->connected = status;
1337 		}
1338 	}
1339 
1340 	return 0;
1341 }
1342 EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
1343 
1344 /**
1345  * snd_soc_dapm_free - free dapm resources
1346  * @socdev: SoC device
1347  *
1348  * Free all dapm widgets and resources.
1349  */
1350 void snd_soc_dapm_free(struct snd_soc_device *socdev)
1351 {
1352 	struct snd_soc_codec *codec = socdev->codec;
1353 
1354 	snd_soc_dapm_sys_remove(socdev->dev);
1355 	dapm_free_widgets(codec);
1356 }
1357 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
1358 
1359 /* Module information */
1360 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
1361 MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
1362 MODULE_LICENSE("GPL");
1363