/drivers/net/atarilance.c replaced init_module&cleanup_module with module_init&module...
[linux-2.6] / sound / soc / soc-dapm.c
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");